1 // Written in the D programming language. 2 /** 3 Timezone realted data and code. 4 5 Copyright: Copyright (c) 2017, Radu Racariu <radu.racariu@gmail.com> 6 License: $(LINK2 www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 Authors: Radu Racariu 8 **/ 9 module haystack.util.tzdata; 10 11 import std.uni : sicmp; 12 import std.datetime : TimeZone, UTC; 13 14 private string cityName(string fullName) pure 15 { 16 import std.string : lastIndexOf; 17 if (fullName.sicmp("STD") == 0) 18 return "UTC"; 19 return fullName[fullName.lastIndexOf('/') + 1 .. $]; 20 } 21 22 version(Posix) 23 { 24 import std.datetime : PosixTimeZone; 25 26 immutable bool hasTzData; 27 immutable string[string] shortNames; 28 29 shared static this() 30 { 31 import std.string : lineSplitter; 32 import std.stdio : writeln; 33 try 34 { 35 if (PosixTimeZone.getInstalledTZNames().length == 0) 36 { 37 writeln("Warning, no timezone data detected! Falling back to UTC."); 38 } 39 else 40 { 41 hasTzData = true; 42 // data from https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 43 foreach (tzName; import("timzoneList.txt").lineSplitter()) 44 { 45 shortNames[cityName(tzName)] = tzName; 46 } 47 } 48 } 49 catch (Exception e) 50 { 51 hasTzData = false; 52 writeln("Warning, no timezone data detected! Falling back to UTC. Details: ", e); 53 } 54 } 55 56 static immutable(TimeZone) timeZone(string name) 57 { 58 import std.string : indexOf; 59 60 if (!hasTzData || name.sicmp("UTC") == 0 || name.sicmp("STD") == 0) 61 return UTC(); 62 if (name.indexOf('/') != -1) 63 { 64 return PosixTimeZone.getTimeZone(name); 65 } 66 else 67 { 68 const fullName = shortNames[name]; 69 return PosixTimeZone.getTimeZone(fullName); 70 } 71 } 72 73 static string getTimeZoneName(immutable(TimeZone) tz) 74 { 75 return cityName(tz.name.length ? tz.name : tz.stdName); 76 } 77 } 78 79 version (Windows) 80 { 81 import std.datetime : WindowsTimeZone, 82 parseTZConversions, 83 TZConversions; 84 85 /** 86 Implemenst the `TimeZone` interface by adding the accurate time zone info 87 */ 88 final class HaystackTimeZone : TimeZone 89 { 90 immutable this(string tzName, immutable(WindowsTimeZone) windowsTz) 91 { 92 super(windowsTz.name, windowsTz.stdName, windowsTz.dstName); 93 this.cityName = .cityName(tzName); 94 _tzName = tzName; 95 _tz = windowsTz; 96 } 97 98 override @property string name() @safe const nothrow 99 { 100 return _tzName; 101 } 102 103 override bool hasDST() const nothrow @property @safe 104 { 105 return _tz.hasDST(); 106 } 107 108 override bool dstInEffect(long stdTime) const nothrow @safe 109 { 110 return _tz.dstInEffect(stdTime); 111 } 112 113 override long utcToTZ(long stdTime) const nothrow @safe 114 { 115 return _tz.utcToTZ(stdTime); 116 } 117 118 override long tzToUTC(long adjTime) const nothrow @safe 119 { 120 return _tz.tzToUTC(adjTime); 121 } 122 123 immutable(string) cityName; 124 125 private: 126 immutable(string) _tzName; 127 immutable(WindowsTimeZone) _tz; 128 } 129 130 immutable(TimeZone) timeZone(string name) 131 { 132 if (name.sicmp("UTC") == 0) 133 return UTC(); 134 135 if (name in conv.toWindows) 136 return new immutable HaystackTimeZone(name, WindowsTimeZone.getTimeZone(conv.toWindows[name][0])); 137 else 138 return getTimeZone(name); 139 } 140 141 string getTimeZoneName(immutable(TimeZone) tz) 142 { 143 immutable name = tz.name; 144 if (!name.length || name == "Coordinated Universal Time") 145 return "UTC"; 146 if (cast(HaystackTimeZone) tz) 147 return (cast(HaystackTimeZone) tz).cityName; 148 return cityName(name); 149 } 150 151 private immutable(TimeZone) getTimeZone(string name) 152 { 153 immutable tz = name in shortNames; 154 return tz !is null ? *tz : null; 155 } 156 157 private immutable TZConversions conv; 158 private immutable TimeZone[string] shortNames; 159 160 shared static this() 161 { 162 // Source of the definitions: 163 // https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml 164 conv = parseTZConversions(import("windowsZones.xml")); 165 foreach (timeZones; conv.fromWindows.byValue) 166 { 167 foreach (timeZoneName; timeZones) 168 { 169 const shortName = cityName(timeZoneName); 170 const windowsTzNames = conv.toWindows[timeZoneName]; 171 const windowsTzName = windowsTzNames[0]; 172 const windowsTz = WindowsTimeZone.getTimeZone(windowsTzName); 173 const tz = new immutable HaystackTimeZone(timeZoneName, windowsTz); 174 shortNames[shortName] = tz; 175 } 176 } 177 } 178 }