1 // Written in the D programming language
4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
5 Authors: Jonathan M Davis
6 Source: $(PHOBOSSRC std/datetime/_timezone.d)
8 module std
.datetime
.timezone
;
11 import std
.datetime
.date
;
12 import std
.datetime
.systime
;
13 import std
.exception
: enforce
;
14 import std
.range
.primitives
;
15 import std
.traits
: isIntegral
, isSomeString
, Unqual
;
19 import core
.stdc
.time
: time_t
;
20 import core
.sys
.windows
.windows
;
21 import core
.sys
.windows
.winsock2
;
22 import std
.windows
.registry
;
24 // Uncomment and run unittests to print missing Windows TZ translations.
25 // Please subscribe to Microsoft Daylight Saving Time & Time Zone Blog
26 // (https://blogs.technet.microsoft.com/dst2007/) if you feel responsible
27 // for updating the translations.
28 // version = UpdateWindowsTZTranslations;
32 import core
.sys
.posix
.signal
: timespec
;
33 import core
.sys
.posix
.sys
.types
: time_t
;
36 version (unittest) import std
.exception
: assertThrown
;
40 Represents a time zone. It is used with $(REF SysTime,std,datetime,systime)
41 to indicate the time zone of a $(REF SysTime,std,datetime,systime).
43 abstract class TimeZone
48 The name of the time zone per the TZ Database. This is the name used to
49 get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone).
52 $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
54 $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of
57 @property string
name() @safe const nothrow
64 Typically, the abbreviation (generally 3 or 4 letters) for the time zone
65 when DST is $(I not) in effect (e.g. PST). It is not necessarily unique.
67 However, on Windows, it may be the unabbreviated name (e.g. Pacific
68 Standard Time). Regardless, it is not the same as name.
70 @property string
stdName() @safe const nothrow
77 Typically, the abbreviation (generally 3 or 4 letters) for the time zone
78 when DST $(I is) in effect (e.g. PDT). It is not necessarily unique.
80 However, on Windows, it may be the unabbreviated name (e.g. Pacific
81 Daylight Time). Regardless, it is not the same as name.
83 @property string
dstName() @safe const nothrow
90 Whether this time zone has Daylight Savings Time at any point in time.
91 Note that for some time zone types it may not have DST for current dates
92 but will still return true for $(D hasDST) because the time zone did at
95 @property abstract bool hasDST() @safe const nothrow;
99 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
100 in UTC time (i.e. std time) and returns whether DST is effect in this
101 time zone at the given point in time.
104 stdTime = The UTC time that needs to be checked for DST in this time
107 abstract bool dstInEffect(long stdTime
) @safe const nothrow;
111 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
112 in UTC time (i.e. std time) and converts it to this time zone's time.
115 stdTime = The UTC time that needs to be adjusted to this time zone's
118 abstract long utcToTZ(long stdTime
) @safe const nothrow;
122 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
123 in this time zone's time and converts it to UTC (i.e. std time).
126 adjTime = The time in this time zone that needs to be adjusted to
129 abstract long tzToUTC(long adjTime
) @safe const nothrow;
133 Returns what the offset from UTC is at the given std time.
134 It includes the DST offset in effect at that time (if any).
137 stdTime = The UTC time for which to get the offset from UTC for this
140 Duration
utcOffsetAt(long stdTime
) @safe const nothrow
142 return dur
!"hnsecs"(utcToTZ(stdTime
) - stdTime
);
145 // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
146 deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead")
147 static immutable(TimeZone
) getTimeZone(string name
) @safe
150 return PosixTimeZone
.getTimeZone(name
);
151 else version (Windows
)
153 import std
.format
: format
;
154 auto windowsTZName
= tzDatabaseNameToWindowsTZName(name
);
155 if (windowsTZName
!= null)
158 return WindowsTimeZone
.getTimeZone(windowsTZName
);
159 catch (DateTimeException dte
)
161 auto oldName
= _getOldName(windowsTZName
);
163 return WindowsTimeZone
.getTimeZone(oldName
);
168 throw new DateTimeException(format("%s does not have an equivalent Windows time zone.", name
));
173 deprecated @safe unittest
175 auto tz
= TimeZone
.getTimeZone("America/Los_Angeles");
178 // The purpose of this is to handle the case where a Windows time zone is
179 // new and exists on an up-to-date Windows box but does not exist on Windows
180 // boxes which have not been properly updated. The "date added" is included
181 // on the theory that we'll be able to remove them at some point in the
182 // the future once enough time has passed, and that way, we know how much
184 private static string
_getOldName(string windowsTZName
) @safe pure nothrow
186 switch (windowsTZName
)
188 case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08
189 case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08
190 case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08
191 case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08
192 default: return null;
196 // Since reading in the time zone files could be expensive, most unit tests
197 // are consolidated into this one unittest block which minimizes how often
198 // it reads a time zone file.
201 import core
.exception
: AssertError
;
202 import std
.conv
: to
;
203 import std
.file
: exists
, isFile
;
204 import std
.format
: format
;
205 import std
.path
: chainPath
;
206 import std
.stdio
: writefln
;
207 import std
.typecons
: tuple
;
209 version (Posix
) alias getTimeZone
= PosixTimeZone
.getTimeZone
;
210 else version (Windows
) alias getTimeZone
= WindowsTimeZone
.getTimeZone
;
212 version (Posix
) scope(exit
) clearTZEnvVar();
214 static immutable(TimeZone
) testTZ(string tzName
,
221 scope(failure
) writefln("Failed time zone: %s", tzName
);
225 immutable tz
= PosixTimeZone
.getTimeZone(tzName
);
226 assert(tz
.name
== tzName
);
228 else version (Windows
)
230 immutable tz
= WindowsTimeZone
.getTimeZone(tzName
);
231 assert(tz
.name
== stdName
);
234 immutable hasDST
= dstOffset
!= Duration
.zero
;
236 //assert(tz.stdName == stdName); //Locale-dependent
237 //assert(tz.dstName == dstName); //Locale-dependent
238 assert(tz
.hasDST
== hasDST
);
240 immutable stdDate
= DateTime(2010, north ?
1 : 7, 1, 6, 0, 0);
241 immutable dstDate
= DateTime(2010, north ?
7 : 1, 1, 6, 0, 0);
242 auto std
= SysTime(stdDate
, tz
);
243 auto dst
= SysTime(dstDate
, tz
);
244 auto stdUTC
= SysTime(stdDate
- utcOffset
, UTC());
245 auto dstUTC
= SysTime(stdDate
- utcOffset
+ dstOffset
, UTC());
247 assert(!std
.dstInEffect
);
248 assert(dst
.dstInEffect
== hasDST
);
249 assert(tz
.utcOffsetAt(std
.stdTime
) == utcOffset
);
250 assert(tz
.utcOffsetAt(dst
.stdTime
) == utcOffset
+ dstOffset
);
252 assert(cast(DateTime
) std
== stdDate
);
253 assert(cast(DateTime
) dst
== dstDate
);
254 assert(std
== stdUTC
);
260 static void testTM(in SysTime st
)
262 import core
.stdc
.time
: localtime
, tm
;
263 time_t unixTime
= st
.toUnixTime();
264 tm
* osTimeInfo
= localtime(&unixTime
);
265 tm ourTimeInfo
= st
.toTM();
267 assert(ourTimeInfo
.tm_sec
== osTimeInfo
.tm_sec
);
268 assert(ourTimeInfo
.tm_min
== osTimeInfo
.tm_min
);
269 assert(ourTimeInfo
.tm_hour
== osTimeInfo
.tm_hour
);
270 assert(ourTimeInfo
.tm_mday
== osTimeInfo
.tm_mday
);
271 assert(ourTimeInfo
.tm_mon
== osTimeInfo
.tm_mon
);
272 assert(ourTimeInfo
.tm_year
== osTimeInfo
.tm_year
);
273 assert(ourTimeInfo
.tm_wday
== osTimeInfo
.tm_wday
);
274 assert(ourTimeInfo
.tm_yday
== osTimeInfo
.tm_yday
);
275 assert(ourTimeInfo
.tm_isdst
== osTimeInfo
.tm_isdst
);
276 assert(ourTimeInfo
.tm_gmtoff
== osTimeInfo
.tm_gmtoff
);
277 assert(to
!string(ourTimeInfo
.tm_zone
) == to
!string(osTimeInfo
.tm_zone
));
283 // Apparently, right/ does not exist on Mac OS X. I don't know
284 // whether or not it exists on FreeBSD. It's rather pointless
285 // normally, since the Posix standard requires that leap seconds
286 // be ignored, so it does make some sense that right/ wouldn't
287 // be there, but since PosixTimeZone _does_ use leap seconds if
288 // the time zone file does, we'll test that functionality if the
289 // appropriate files exist.
290 if (chainPath(PosixTimeZone
.defaultTZDatabaseDir
, "right", tzName
).exists
)
292 auto leapTZ
= PosixTimeZone
.getTimeZone("right/" ~ tzName
);
294 assert(leapTZ
.name
== "right/" ~ tzName
);
295 //assert(leapTZ.stdName == stdName); //Locale-dependent
296 //assert(leapTZ.dstName == dstName); //Locale-dependent
297 assert(leapTZ
.hasDST
== hasDST
);
299 auto leapSTD
= SysTime(std
.stdTime
, leapTZ
);
300 auto leapDST
= SysTime(dst
.stdTime
, leapTZ
);
302 assert(!leapSTD
.dstInEffect
);
303 assert(leapDST
.dstInEffect
== hasDST
);
305 assert(leapSTD
.stdTime
== std
.stdTime
);
306 assert(leapDST
.stdTime
== dst
.stdTime
);
308 // Whenever a leap second is added/removed,
309 // this will have to be adjusted.
310 //enum leapDiff = convert!("seconds", "hnsecs")(25);
311 //assert(leapSTD.adjTime - leapDiff == std.adjTime);
312 //assert(leapDST.adjTime - leapDiff == dst.adjTime);
319 auto dstSwitches
= [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
320 /+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
321 ///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0),
322 /+Europe/London+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2),
323 /+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3),
324 /+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)];
328 version (FreeBSD
) enum utcZone
= "Etc/UTC";
329 else version (NetBSD
) enum utcZone
= "UTC";
330 else version (linux
) enum utcZone
= "UTC";
331 else version (OSX
) enum utcZone
= "UTC";
332 else static assert(0, "The location of the UTC timezone file on this Posix platform must be set.");
334 auto tzs
= [testTZ("America/Los_Angeles", "PST", "PDT", dur
!"hours"(-8), dur
!"hours"(1)),
335 testTZ("America/New_York", "EST", "EDT", dur
!"hours"(-5), dur
!"hours"(1)),
336 //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false),
337 testTZ("Europe/London", "GMT", "BST", dur
!"hours"(0), dur
!"hours"(1)),
338 testTZ("Europe/Paris", "CET", "CEST", dur
!"hours"(1), dur
!"hours"(1)),
339 // Per www.timeanddate.com, it should be "CST" and "CDT",
340 // but the OS insists that it's "CST" for both. We should
341 // probably figure out how to report an error in the TZ
342 // database and report it.
343 testTZ("Australia/Adelaide", "CST", "CST",
344 dur
!"hours"(9) + dur
!"minutes"(30), dur
!"hours"(1), false)];
346 testTZ(utcZone
, "UTC", "UTC", dur
!"hours"(0), dur
!"hours"(0));
347 assertThrown
!DateTimeException(PosixTimeZone
.getTimeZone("hello_world"));
349 else version (Windows
)
351 auto tzs
= [testTZ("Pacific Standard Time", "Pacific Standard Time",
352 "Pacific Daylight Time", dur
!"hours"(-8), dur
!"hours"(1)),
353 testTZ("Eastern Standard Time", "Eastern Standard Time",
354 "Eastern Daylight Time", dur
!"hours"(-5), dur
!"hours"(1)),
355 //testTZ("Pacific SA Standard Time", "Pacific SA Standard Time",
356 //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false),
357 testTZ("GMT Standard Time", "GMT Standard Time",
358 "GMT Daylight Time", dur
!"hours"(0), dur
!"hours"(1)),
359 testTZ("Romance Standard Time", "Romance Standard Time",
360 "Romance Daylight Time", dur
!"hours"(1), dur
!"hours"(1)),
361 testTZ("Cen. Australia Standard Time", "Cen. Australia Standard Time",
362 "Cen. Australia Daylight Time",
363 dur
!"hours"(9) + dur
!"minutes"(30), dur
!"hours"(1), false)];
365 testTZ("Greenwich Standard Time", "Greenwich Standard Time",
366 "Greenwich Daylight Time", dur
!"hours"(0), dur
!"hours"(0));
367 assertThrown
!DateTimeException(WindowsTimeZone
.getTimeZone("hello_world"));
370 assert(0, "OS not supported.");
372 foreach (i
; 0 .. tzs
.length
)
375 immutable spring
= dstSwitches
[i
][2];
376 immutable fall
= dstSwitches
[i
][3];
377 auto stdOffset
= SysTime(dstSwitches
[i
][0] + dur
!"days"(-1), tz
).utcOffset
;
378 auto dstOffset
= stdOffset
+ dur
!"hours"(1);
380 // Verify that creating a SysTime in the given time zone results
381 // in a SysTime with the correct std time during and surrounding
383 foreach (hour
; -12 .. 13)
385 auto st
= SysTime(dstSwitches
[i
][0] + dur
!"hours"(hour
), tz
);
386 immutable targetHour
= hour
< 0 ? hour
+ 24 : hour
;
388 static void testHour(SysTime st
, int hour
, string tzName
, size_t line
= __LINE__
)
390 enforce(st
.hour
== hour
,
391 new AssertError(format("[%s] [%s]: [%s] [%s]", st
, tzName
, st
.hour
, hour
),
395 void testOffset1(Duration offset
, bool dstInEffect
, size_t line
= __LINE__
)
397 AssertError
msg(string tag
)
399 return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]",
400 tag
, st
, tz
.name
, st
.utcOffset
, stdOffset
, dstOffset
),
404 enforce(st
.dstInEffect
== dstInEffect
, msg("1"));
405 enforce(st
.utcOffset
== offset
, msg("2"));
406 enforce((st
+ dur
!"minutes"(1)).utcOffset
== offset
, msg("3"));
411 testHour(st
, spring
+ 1, tz
.name
);
412 testHour(st
+ dur
!"minutes"(1), spring
+ 1, tz
.name
);
416 testHour(st
, targetHour
, tz
.name
);
417 testHour(st
+ dur
!"minutes"(1), targetHour
, tz
.name
);
421 testOffset1(stdOffset
, false);
423 testOffset1(dstOffset
, true);
425 st
= SysTime(dstSwitches
[i
][1] + dur
!"hours"(hour
), tz
);
426 testHour(st
, targetHour
, tz
.name
);
428 // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is).
429 if (hour
== fall
- 1)
430 testHour(st
+ dur
!"hours"(1), targetHour
, tz
.name
);
433 testOffset1(dstOffset
, true);
435 testOffset1(stdOffset
, false);
438 // Verify that converting a time in UTC to a time in another
439 // time zone results in the correct time during and surrounding
442 auto springSwitch
= SysTime(dstSwitches
[i
][0] + dur
!"hours"(spring
), UTC()) - stdOffset
;
443 auto fallSwitch
= SysTime(dstSwitches
[i
][1] + dur
!"hours"(fall
), UTC()) - dstOffset
;
444 // @@@BUG@@@ 3659 makes this necessary.
445 auto fallSwitchMinus1
= fallSwitch
- dur
!"hours"(1);
447 foreach (hour
; -24 .. 25)
449 auto utc
= SysTime(dstSwitches
[i
][0] + dur
!"hours"(hour
), UTC());
450 auto local
= utc
.toOtherTZ(tz
);
452 void testOffset2(Duration offset
, size_t line
= __LINE__
)
454 AssertError
msg(string tag
)
456 return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag
, hour
, tz
.name
, utc
, local
),
460 enforce((utc
+ offset
).hour
== local
.hour
, msg("1"));
461 enforce((utc
+ offset
+ dur
!"minutes"(1)).hour
== local
.hour
, msg("2"));
464 if (utc
< springSwitch
)
465 testOffset2(stdOffset
);
467 testOffset2(dstOffset
);
469 utc
= SysTime(dstSwitches
[i
][1] + dur
!"hours"(hour
), UTC());
470 local
= utc
.toOtherTZ(tz
);
472 if (utc
== fallSwitch || utc
== fallSwitchMinus1
)
476 testOffset2(dstOffset
);
480 testOffset2(stdOffset
);
482 else if (utc
> fallSwitch
)
483 testOffset2(stdOffset
);
485 testOffset2(dstOffset
);
491 // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
492 deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead")
493 static string
[] getInstalledTZNames(string subName
= "") @safe
496 return PosixTimeZone
.getInstalledTZNames(subName
);
497 else version (Windows
)
499 import std
.algorithm
.searching
: startsWith
;
500 import std
.algorithm
.sorting
: sort
;
501 import std
.array
: appender
;
503 auto windowsNames
= WindowsTimeZone
.getInstalledTZNames();
504 auto retval
= appender
!(string
[])();
506 foreach (winName
; windowsNames
)
508 auto tzName
= windowsTZNameToTZDatabaseName(winName
);
509 if (tzName
!is null && tzName
.startsWith(subName
))
518 deprecated @safe unittest
520 import std
.exception
: assertNotThrown
;
521 import std
.stdio
: writefln
;
522 static void testPZSuccess(string tzName
)
524 scope(failure
) writefln("TZName which threw: %s", tzName
);
525 TimeZone
.getTimeZone(tzName
);
528 auto tzNames
= getInstalledTZNames();
529 // This was not previously tested, and it's currently failing, so I'm
530 // leaving it commented out until I can sort it out.
531 //assert(equal(tzNames, tzNames.uniq()));
533 foreach (tzName
; tzNames
)
534 assertNotThrown
!DateTimeException(testPZSuccess(tzName
));
542 name = The name of the time zone.
543 stdName = The abbreviation for the time zone during std time.
544 dstName = The abbreviation for the time zone during DST.
546 this(string name
, string stdName
, string dstName
) @safe immutable pure
556 immutable string _name
;
557 immutable string _stdName
;
558 immutable string _dstName
;
563 A TimeZone which represents the current local time zone on
564 the system running your program.
566 This uses the underlying C calls to adjust the time rather than using
567 specific D code based off of system settings to calculate the time such as
568 $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that
569 it will use whatever the current time zone is on the system, even if the
570 system's time zone changes while the program is running.
572 final class LocalTime
: TimeZone
577 $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its
580 static immutable(LocalTime
) opCall() @trusted pure nothrow
582 alias FuncType
= @safe pure nothrow immutable(LocalTime
) function();
583 return (cast(FuncType
)&singleton
)();
590 The name of the time zone per the TZ Database. This is the name used
591 to get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone).
593 Note that this always returns the empty string. This is because time
594 zones cannot be uniquely identified by the attributes given by the
595 OS (such as the $(D stdName) and $(D dstName)), and neither Posix
596 systems nor Windows systems provide an easy way to get the TZ
597 Database name of the local time zone.
600 $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
602 $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List
605 @property override string
name() @safe const nothrow;
610 Typically, the abbreviation (generally 3 or 4 letters) for the time zone
611 when DST is $(I not) in effect (e.g. PST). It is not necessarily unique.
613 However, on Windows, it may be the unabbreviated name (e.g. Pacific
614 Standard Time). Regardless, it is not the same as name.
616 This property is overridden because the local time of the system could
617 change while the program is running and we need to determine it
618 dynamically rather than it being fixed like it would be with most time
621 @property override string
stdName() @trusted const nothrow
625 import core
.stdc
.time
: tzname
;
626 import std
.conv
: to
;
628 return to
!string(tzname
[0]);
630 assert(0, "to!string(tzname[0]) failed.");
632 else version (Windows
)
634 TIME_ZONE_INFORMATION tzInfo
;
635 GetTimeZoneInformation(&tzInfo
);
637 // Cannot use to!string() like this should, probably due to bug
638 // http://d.puremagic.com/issues/show_bug.cgi?id=5016
639 //return to!string(tzInfo.StandardName);
643 foreach (i
, ref wchar c
; str)
644 c
= tzInfo
.StandardName
[i
];
650 foreach (dchar c
; str)
661 assert(0, "GetTimeZoneInformation() returned invalid UTF-16.");
669 // A bug on FreeBSD 9+ makes it so that this test fails.
670 // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862
672 else version (NetBSD
)
674 // The same bug on NetBSD 7+
678 assert(LocalTime().stdName
!is null);
682 scope(exit
) clearTZEnvVar();
684 setTZEnvVar("America/Los_Angeles");
685 assert(LocalTime().stdName
== "PST");
687 setTZEnvVar("America/New_York");
688 assert(LocalTime().stdName
== "EST");
695 Typically, the abbreviation (generally 3 or 4 letters) for the time zone
696 when DST $(I is) in effect (e.g. PDT). It is not necessarily unique.
698 However, on Windows, it may be the unabbreviated name (e.g. Pacific
699 Daylight Time). Regardless, it is not the same as name.
701 This property is overridden because the local time of the system could
702 change while the program is running and we need to determine it
703 dynamically rather than it being fixed like it would be with most time
706 @property override string
dstName() @trusted const nothrow
710 import core
.stdc
.time
: tzname
;
711 import std
.conv
: to
;
713 return to
!string(tzname
[1]);
715 assert(0, "to!string(tzname[1]) failed.");
717 else version (Windows
)
719 TIME_ZONE_INFORMATION tzInfo
;
720 GetTimeZoneInformation(&tzInfo
);
722 // Cannot use to!string() like this should, probably due to bug
723 // http://d.puremagic.com/issues/show_bug.cgi?id=5016
724 //return to!string(tzInfo.DaylightName);
728 foreach (i
, ref wchar c
; str)
729 c
= tzInfo
.DaylightName
[i
];
735 foreach (dchar c
; str)
746 assert(0, "GetTimeZoneInformation() returned invalid UTF-16.");
752 assert(LocalTime().dstName
!is null);
756 scope(exit
) clearTZEnvVar();
760 // A bug on FreeBSD 9+ makes it so that this test fails.
761 // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862
763 else version (NetBSD
)
765 // The same bug on NetBSD 7+
769 setTZEnvVar("America/Los_Angeles");
770 assert(LocalTime().dstName
== "PDT");
772 setTZEnvVar("America/New_York");
773 assert(LocalTime().dstName
== "EDT");
780 Whether this time zone has Daylight Savings Time at any point in time.
781 Note that for some time zone types it may not have DST for current
782 dates but will still return true for $(D hasDST) because the time zone
783 did at some point have DST.
785 @property override bool hasDST() @trusted const nothrow
789 static if (is(typeof(daylight
)))
790 return cast(bool)(daylight
);
795 auto currYear
= (cast(Date
) Clock
.currTime()).year
;
796 auto janOffset
= SysTime(Date(currYear
, 1, 4), cast(immutable) this).stdTime
-
797 SysTime(Date(currYear
, 1, 4), UTC()).stdTime
;
798 auto julyOffset
= SysTime(Date(currYear
, 7, 4), cast(immutable) this).stdTime
-
799 SysTime(Date(currYear
, 7, 4), UTC()).stdTime
;
801 return janOffset
!= julyOffset
;
804 assert(0, "Clock.currTime() threw.");
807 else version (Windows
)
809 TIME_ZONE_INFORMATION tzInfo
;
810 GetTimeZoneInformation(&tzInfo
);
812 return tzInfo
.DaylightDate
.wMonth
!= 0;
822 scope(exit
) clearTZEnvVar();
824 setTZEnvVar("America/Los_Angeles");
825 assert(LocalTime().hasDST
);
827 setTZEnvVar("America/New_York");
828 assert(LocalTime().hasDST
);
831 assert(!LocalTime().hasDST
);
837 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
838 in UTC time (i.e. std time) and returns whether DST is in effect in this
839 time zone at the given point in time.
842 stdTime = The UTC time that needs to be checked for DST in this time
845 override bool dstInEffect(long stdTime
) @trusted const nothrow
847 import core
.stdc
.time
: localtime
, tm
;
848 time_t unixTime
= stdTimeToUnixTime(stdTime
);
852 tm
* timeInfo
= localtime(&unixTime
);
854 return cast(bool)(timeInfo
.tm_isdst
);
856 else version (Windows
)
858 // Apparently Windows isn't smart enough to deal with negative time_t.
861 tm
* timeInfo
= localtime(&unixTime
);
864 return cast(bool)(timeInfo
.tm_isdst
);
867 TIME_ZONE_INFORMATION tzInfo
;
868 GetTimeZoneInformation(&tzInfo
);
870 return WindowsTimeZone
._dstInEffect(&tzInfo
, stdTime
);
876 auto currTime
= Clock
.currStdTime
;
877 LocalTime().dstInEffect(currTime
);
882 Returns hnsecs in the local time zone using the standard C function
883 calls on Posix systems and the standard Windows system calls on Windows
884 systems to adjust the time to the appropriate time zone from std time.
887 stdTime = The UTC time that needs to be adjusted to this time zone's
891 $(D TimeZone.utcToTZ)
893 override long utcToTZ(long stdTime
) @trusted const nothrow
896 return stdTime
+ convert
!("seconds", "hnsecs")(tm_gmtoff(stdTime
));
899 import core
.stdc
.time
: localtime
, tm
;
900 time_t unixTime
= stdTimeToUnixTime(stdTime
);
901 tm
* timeInfo
= localtime(&unixTime
);
903 return stdTime
+ convert
!("seconds", "hnsecs")(timeInfo
.tm_gmtoff
);
905 else version (Windows
)
907 TIME_ZONE_INFORMATION tzInfo
;
908 GetTimeZoneInformation(&tzInfo
);
910 return WindowsTimeZone
._utcToTZ(&tzInfo
, stdTime
, hasDST
);
916 LocalTime().utcToTZ(0);
921 Returns std time using the standard C function calls on Posix systems
922 and the standard Windows system calls on Windows systems to adjust the
923 time to UTC from the appropriate time zone.
926 $(D TimeZone.tzToUTC)
929 adjTime = The time in this time zone that needs to be adjusted to
932 override long tzToUTC(long adjTime
) @trusted const nothrow
936 import core
.stdc
.time
: localtime
, tm
;
937 time_t unixTime
= stdTimeToUnixTime(adjTime
);
939 immutable past
= unixTime
- cast(time_t
) convert
!("days", "seconds")(1);
940 tm
* timeInfo
= localtime(past
< unixTime ?
&past
: &unixTime
);
941 immutable pastOffset
= timeInfo
.tm_gmtoff
;
943 immutable future
= unixTime
+ cast(time_t
) convert
!("days", "seconds")(1);
944 timeInfo
= localtime(future
> unixTime ?
&future
: &unixTime
);
945 immutable futureOffset
= timeInfo
.tm_gmtoff
;
947 if (pastOffset
== futureOffset
)
948 return adjTime
- convert
!("seconds", "hnsecs")(pastOffset
);
950 if (pastOffset
< futureOffset
)
951 unixTime
-= cast(time_t
) convert
!("hours", "seconds")(1);
953 unixTime
-= pastOffset
;
954 timeInfo
= localtime(&unixTime
);
956 return adjTime
- convert
!("seconds", "hnsecs")(timeInfo
.tm_gmtoff
);
958 else version (Windows
)
960 TIME_ZONE_INFORMATION tzInfo
;
961 GetTimeZoneInformation(&tzInfo
);
963 return WindowsTimeZone
._tzToUTC(&tzInfo
, adjTime
, hasDST
);
969 import core
.exception
: AssertError
;
970 import std
.format
: format
;
971 import std
.typecons
: tuple
;
973 assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0);
974 assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0);
976 assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0);
977 assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0);
981 scope(exit
) clearTZEnvVar();
983 auto tzInfos
= [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
984 tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2),
985 //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0),
986 tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1),
987 tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2),
988 tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3),
989 tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)];
991 foreach (i
; 0 .. tzInfos
.length
)
993 auto tzName
= tzInfos
[i
][0];
995 immutable spring
= tzInfos
[i
][3];
996 immutable fall
= tzInfos
[i
][4];
997 auto stdOffset
= SysTime(tzInfos
[i
][1] + dur
!"hours"(-12)).utcOffset
;
998 auto dstOffset
= stdOffset
+ dur
!"hours"(1);
1000 // Verify that creating a SysTime in the given time zone results
1001 // in a SysTime with the correct std time during and surrounding
1003 foreach (hour
; -12 .. 13)
1005 auto st
= SysTime(tzInfos
[i
][1] + dur
!"hours"(hour
));
1006 immutable targetHour
= hour
< 0 ? hour
+ 24 : hour
;
1008 static void testHour(SysTime st
, int hour
, string tzName
, size_t line
= __LINE__
)
1010 enforce(st
.hour
== hour
,
1011 new AssertError(format("[%s] [%s]: [%s] [%s]", st
, tzName
, st
.hour
, hour
),
1015 void testOffset1(Duration offset
, bool dstInEffect
, size_t line
= __LINE__
)
1017 AssertError
msg(string tag
)
1019 return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]",
1020 tag
, st
, tzName
, st
.utcOffset
, stdOffset
, dstOffset
),
1024 enforce(st
.dstInEffect
== dstInEffect
, msg("1"));
1025 enforce(st
.utcOffset
== offset
, msg("2"));
1026 enforce((st
+ dur
!"minutes"(1)).utcOffset
== offset
, msg("3"));
1031 testHour(st
, spring
+ 1, tzName
);
1032 testHour(st
+ dur
!"minutes"(1), spring
+ 1, tzName
);
1036 testHour(st
, targetHour
, tzName
);
1037 testHour(st
+ dur
!"minutes"(1), targetHour
, tzName
);
1041 testOffset1(stdOffset
, false);
1043 testOffset1(dstOffset
, true);
1045 st
= SysTime(tzInfos
[i
][2] + dur
!"hours"(hour
));
1046 testHour(st
, targetHour
, tzName
);
1048 // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is).
1049 if (hour
== fall
- 1)
1050 testHour(st
+ dur
!"hours"(1), targetHour
, tzName
);
1053 testOffset1(dstOffset
, true);
1055 testOffset1(stdOffset
, false);
1058 // Verify that converting a time in UTC to a time in another
1059 // time zone results in the correct time during and surrounding
1062 auto springSwitch
= SysTime(tzInfos
[i
][1] + dur
!"hours"(spring
), UTC()) - stdOffset
;
1063 auto fallSwitch
= SysTime(tzInfos
[i
][2] + dur
!"hours"(fall
), UTC()) - dstOffset
;
1064 // @@@BUG@@@ 3659 makes this necessary.
1065 auto fallSwitchMinus1
= fallSwitch
- dur
!"hours"(1);
1067 foreach (hour
; -24 .. 25)
1069 auto utc
= SysTime(tzInfos
[i
][1] + dur
!"hours"(hour
), UTC());
1070 auto local
= utc
.toLocalTime();
1072 void testOffset2(Duration offset
, size_t line
= __LINE__
)
1074 AssertError
msg(string tag
)
1076 return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag
, hour
, tzName
, utc
, local
),
1080 enforce((utc
+ offset
).hour
== local
.hour
, msg("1"));
1081 enforce((utc
+ offset
+ dur
!"minutes"(1)).hour
== local
.hour
, msg("2"));
1084 if (utc
< springSwitch
)
1085 testOffset2(stdOffset
);
1087 testOffset2(dstOffset
);
1089 utc
= SysTime(tzInfos
[i
][2] + dur
!"hours"(hour
), UTC());
1090 local
= utc
.toLocalTime();
1092 if (utc
== fallSwitch || utc
== fallSwitchMinus1
)
1096 testOffset2(dstOffset
);
1100 testOffset2(stdOffset
);
1102 else if (utc
> fallSwitch
)
1103 testOffset2(stdOffset
);
1105 testOffset2(dstOffset
);
1114 this() @safe immutable pure
1120 // This is done so that we can maintain purity in spite of doing an impure
1121 // operation the first time that LocalTime() is called.
1122 static immutable(LocalTime
) singleton() @trusted
1124 import core
.stdc
.time
: tzset
;
1125 import std
.concurrency
: initOnce
;
1126 static instance
= new immutable(LocalTime
)();
1127 static shared bool guard
;
1128 initOnce
!guard({tzset(); return true;}());
1133 // The Solaris version of struct tm has no tm_gmtoff field, so do it here
1136 long tm_gmtoff(long stdTime
) @trusted const nothrow
1138 import core
.stdc
.time
: localtime
, gmtime
, tm
;
1140 time_t unixTime
= stdTimeToUnixTime(stdTime
);
1141 tm
* buf
= localtime(&unixTime
);
1143 buf
= gmtime(&unixTime
);
1144 tm timeInfoGmt
= *buf
;
1146 return timeInfo
.tm_sec
- timeInfoGmt
.tm_sec
+
1147 convert
!("minutes", "seconds")(timeInfo
.tm_min
- timeInfoGmt
.tm_min
) +
1148 convert
!("hours", "seconds")(timeInfo
.tm_hour
- timeInfoGmt
.tm_hour
);
1155 A $(LREF TimeZone) which represents UTC.
1157 final class UTC
: TimeZone
1162 $(D UTC) is a singleton class. $(D UTC) returns its only instance.
1164 static immutable(UTC
) opCall() @safe pure nothrow
1171 Always returns false.
1173 @property override bool hasDST() @safe const nothrow
1180 Always returns false.
1182 override bool dstInEffect(long stdTime
) @safe const nothrow
1189 Returns the given hnsecs without changing them at all.
1192 stdTime = The UTC time that needs to be adjusted to this time zone's
1196 $(D TimeZone.utcToTZ)
1198 override long utcToTZ(long stdTime
) @safe const nothrow
1205 assert(UTC().utcToTZ(0) == 0);
1209 scope(exit
) clearTZEnvVar();
1212 auto std
= SysTime(Date(2010, 1, 1));
1213 auto dst
= SysTime(Date(2010, 7, 1));
1214 assert(UTC().utcToTZ(std
.stdTime
) == std
.stdTime
);
1215 assert(UTC().utcToTZ(dst
.stdTime
) == dst
.stdTime
);
1221 Returns the given hnsecs without changing them at all.
1224 $(D TimeZone.tzToUTC)
1227 adjTime = The time in this time zone that needs to be adjusted to
1230 override long tzToUTC(long adjTime
) @safe const nothrow
1237 assert(UTC().tzToUTC(0) == 0);
1241 scope(exit
) clearTZEnvVar();
1244 auto std
= SysTime(Date(2010, 1, 1));
1245 auto dst
= SysTime(Date(2010, 7, 1));
1246 assert(UTC().tzToUTC(std
.stdTime
) == std
.stdTime
);
1247 assert(UTC().tzToUTC(dst
.stdTime
) == dst
.stdTime
);
1253 Returns a $(REF Duration, core,time) of 0.
1256 stdTime = The UTC time for which to get the offset from UTC for this
1259 override Duration
utcOffsetAt(long stdTime
) @safe const nothrow
1261 return dur
!"hnsecs"(0);
1267 this() @safe immutable pure
1269 super("UTC", "UTC", "UTC");
1273 static immutable UTC _utc
= new immutable(UTC
)();
1278 Represents a time zone with an offset (in minutes, west is negative) from
1281 It's primarily used as the time zone in the result of
1282 $(REF SysTime,std,datetime,systime)'s $(D fromISOString),
1283 $(D fromISOExtString), and $(D fromSimpleString).
1285 $(D name) and $(D dstName) are always the empty string since this time zone
1286 has no DST, and while it may be meant to represent a time zone which is in
1287 the TZ Database, obviously it's not likely to be following the exact rules
1288 of any of the time zones in the TZ Database, so it makes no sense to set it.
1290 final class SimpleTimeZone
: TimeZone
1295 Always returns false.
1297 @property override bool hasDST() @safe const nothrow
1304 Always returns false.
1306 override bool dstInEffect(long stdTime
) @safe const nothrow
1313 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
1314 in UTC time (i.e. std time) and converts it to this time zone's time.
1317 stdTime = The UTC time that needs to be adjusted to this time zone's
1320 override long utcToTZ(long stdTime
) @safe const nothrow
1322 return stdTime
+ _utcOffset
.total
!"hnsecs";
1327 auto west
= new immutable SimpleTimeZone(dur
!"hours"(-8));
1328 auto east
= new immutable SimpleTimeZone(dur
!"hours"(8));
1330 assert(west
.utcToTZ(0) == -288_000_000_000L);
1331 assert(east
.utcToTZ(0) == 288_000_000_000L);
1332 assert(west
.utcToTZ(54_321_234_567_890L) == 54_033_234_567_890L);
1335 assert(cstz
.utcToTZ(50002) == west
.utcToTZ(50002));
1340 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
1341 in this time zone's time and converts it to UTC (i.e. std time).
1344 adjTime = The time in this time zone that needs to be adjusted to
1347 override long tzToUTC(long adjTime
) @safe const nothrow
1349 return adjTime
- _utcOffset
.total
!"hnsecs";
1354 auto west
= new immutable SimpleTimeZone(dur
!"hours"(-8));
1355 auto east
= new immutable SimpleTimeZone(dur
!"hours"(8));
1357 assert(west
.tzToUTC(-288_000_000_000L) == 0);
1358 assert(east
.tzToUTC(288_000_000_000L) == 0);
1359 assert(west
.tzToUTC(54_033_234_567_890L) == 54_321_234_567_890L);
1362 assert(cstz
.tzToUTC(20005) == west
.tzToUTC(20005));
1367 Returns utcOffset as a $(REF Duration, core,time).
1370 stdTime = The UTC time for which to get the offset from UTC for this
1373 override Duration
utcOffsetAt(long stdTime
) @safe const nothrow
1381 utcOffset = This time zone's offset from UTC with west of UTC being
1382 negative (it is added to UTC to get the adjusted time).
1383 stdName = The $(D stdName) for this time zone.
1385 this(Duration utcOffset
, string stdName
= "") @safe immutable pure
1387 // FIXME This probably needs to be changed to something like (-12 - 13).
1388 enforce
!DateTimeException(abs(utcOffset
) < dur
!"minutes"(1440),
1389 "Offset from UTC must be within range (-24:00 - 24:00).");
1390 super("", stdName
, "");
1391 this._utcOffset
= utcOffset
;
1396 auto stz
= new immutable SimpleTimeZone(dur
!"hours"(-8), "PST");
1397 assert(stz
.name
== "");
1398 assert(stz
.stdName
== "PST");
1399 assert(stz
.dstName
== "");
1400 assert(stz
.utcOffset
== dur
!"hours"(-8));
1405 The amount of time the offset from UTC is (negative is west of UTC,
1408 @property Duration
utcOffset() @safe const pure nothrow
1417 Returns a time zone as a string with an offset from UTC.
1419 Time zone offsets will be in the form +HHMM or -HHMM.
1422 utcOffset = The number of minutes offset from UTC (negative means
1425 static string
toISOString(Duration utcOffset
) @safe pure
1427 import std
.format
: format
;
1428 immutable absOffset
= abs(utcOffset
);
1429 enforce
!DateTimeException(absOffset
< dur
!"minutes"(1440),
1430 "Offset from UTC must be within range (-24:00 - 24:00).");
1433 absOffset
.split
!("hours", "minutes")(hours
, minutes
);
1434 return format(utcOffset
< Duration
.zero ?
"-%02d%02d" : "+%02d%02d", hours
, minutes
);
1439 static string
testSTZInvalid(Duration offset
)
1441 return SimpleTimeZone
.toISOString(offset
);
1444 assertThrown
!DateTimeException(testSTZInvalid(dur
!"minutes"(1440)));
1445 assertThrown
!DateTimeException(testSTZInvalid(dur
!"minutes"(-1440)));
1447 assert(toISOString(dur
!"minutes"(0)) == "+0000");
1448 assert(toISOString(dur
!"minutes"(1)) == "+0001");
1449 assert(toISOString(dur
!"minutes"(10)) == "+0010");
1450 assert(toISOString(dur
!"minutes"(59)) == "+0059");
1451 assert(toISOString(dur
!"minutes"(60)) == "+0100");
1452 assert(toISOString(dur
!"minutes"(90)) == "+0130");
1453 assert(toISOString(dur
!"minutes"(120)) == "+0200");
1454 assert(toISOString(dur
!"minutes"(480)) == "+0800");
1455 assert(toISOString(dur
!"minutes"(1439)) == "+2359");
1457 assert(toISOString(dur
!"minutes"(-1)) == "-0001");
1458 assert(toISOString(dur
!"minutes"(-10)) == "-0010");
1459 assert(toISOString(dur
!"minutes"(-59)) == "-0059");
1460 assert(toISOString(dur
!"minutes"(-60)) == "-0100");
1461 assert(toISOString(dur
!"minutes"(-90)) == "-0130");
1462 assert(toISOString(dur
!"minutes"(-120)) == "-0200");
1463 assert(toISOString(dur
!"minutes"(-480)) == "-0800");
1464 assert(toISOString(dur
!"minutes"(-1439)) == "-2359");
1469 Returns a time zone as a string with an offset from UTC.
1471 Time zone offsets will be in the form +HH:MM or -HH:MM.
1474 utcOffset = The number of minutes offset from UTC (negative means
1477 static string
toISOExtString(Duration utcOffset
) @safe pure
1479 import std
.format
: format
;
1481 immutable absOffset
= abs(utcOffset
);
1482 enforce
!DateTimeException(absOffset
< dur
!"minutes"(1440),
1483 "Offset from UTC must be within range (-24:00 - 24:00).");
1486 absOffset
.split
!("hours", "minutes")(hours
, minutes
);
1487 return format(utcOffset
< Duration
.zero ?
"-%02d:%02d" : "+%02d:%02d", hours
, minutes
);
1492 static string
testSTZInvalid(Duration offset
)
1494 return SimpleTimeZone
.toISOExtString(offset
);
1497 assertThrown
!DateTimeException(testSTZInvalid(dur
!"minutes"(1440)));
1498 assertThrown
!DateTimeException(testSTZInvalid(dur
!"minutes"(-1440)));
1500 assert(toISOExtString(dur
!"minutes"(0)) == "+00:00");
1501 assert(toISOExtString(dur
!"minutes"(1)) == "+00:01");
1502 assert(toISOExtString(dur
!"minutes"(10)) == "+00:10");
1503 assert(toISOExtString(dur
!"minutes"(59)) == "+00:59");
1504 assert(toISOExtString(dur
!"minutes"(60)) == "+01:00");
1505 assert(toISOExtString(dur
!"minutes"(90)) == "+01:30");
1506 assert(toISOExtString(dur
!"minutes"(120)) == "+02:00");
1507 assert(toISOExtString(dur
!"minutes"(480)) == "+08:00");
1508 assert(toISOExtString(dur
!"minutes"(1439)) == "+23:59");
1510 assert(toISOExtString(dur
!"minutes"(-1)) == "-00:01");
1511 assert(toISOExtString(dur
!"minutes"(-10)) == "-00:10");
1512 assert(toISOExtString(dur
!"minutes"(-59)) == "-00:59");
1513 assert(toISOExtString(dur
!"minutes"(-60)) == "-01:00");
1514 assert(toISOExtString(dur
!"minutes"(-90)) == "-01:30");
1515 assert(toISOExtString(dur
!"minutes"(-120)) == "-02:00");
1516 assert(toISOExtString(dur
!"minutes"(-480)) == "-08:00");
1517 assert(toISOExtString(dur
!"minutes"(-1439)) == "-23:59");
1522 Takes a time zone as a string with an offset from UTC and returns a
1523 $(LREF SimpleTimeZone) which matches.
1525 The accepted formats for time zone offsets are +HH, -HH, +HHMM, and
1529 isoString = A string which represents a time zone in the ISO format.
1531 static immutable(SimpleTimeZone
) fromISOString(S
)(S isoString
) @safe pure
1534 import std
.algorithm
.searching
: startsWith
, countUntil
, all
;
1535 import std
.ascii
: isDigit
;
1536 import std
.conv
: to
;
1537 import std
.format
: format
;
1539 auto dstr
= to
!dstring(isoString
);
1541 enforce
!DateTimeException(dstr
.startsWith('-', '+'), "Invalid ISO String");
1543 auto sign
= dstr
.startsWith('-') ?
-1 : 1;
1546 enforce
!DateTimeException(all
!isDigit(dstr
), format("Invalid ISO String: %s", dstr
));
1551 if (dstr
.length
== 2)
1552 hours
= to
!int(dstr
);
1553 else if (dstr
.length
== 4)
1555 hours
= to
!int(dstr
[0 .. 2]);
1556 minutes
= to
!int(dstr
[2 .. 4]);
1559 throw new DateTimeException(format("Invalid ISO String: %s", dstr
));
1561 enforce
!DateTimeException(hours
< 24 && minutes
< 60, format("Invalid ISO String: %s", dstr
));
1563 return new immutable SimpleTimeZone(sign
* (dur
!"hours"(hours
) + dur
!"minutes"(minutes
)));
1568 import core
.exception
: AssertError
;
1569 import std
.format
: format
;
1571 foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1",
1572 "-24:00", "+24:00", "-24", "+24", "-2400", "+2400",
1573 "1", "+1", "-1", "+9", "-9",
1574 "+1:0", "+01:0", "+1:00", "+01:000", "+01:60",
1575 "-1:0", "-01:0", "-1:00", "-01:000", "-01:60",
1576 "000", "00000", "0160", "-0160",
1577 " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ",
1578 " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ",
1579 " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ",
1580 " -0800", "- 0800", "-08 00", "-08 00", "-0800 ",
1581 "+ab:cd", "+abcd", "+0Z:00", "+Z", "+00Z",
1582 "-ab:cd", "+abcd", "-0Z:00", "-Z", "-00Z",
1583 "01:00", "12:00", "23:59"])
1585 assertThrown
!DateTimeException(SimpleTimeZone
.fromISOString(str), format("[%s]", str));
1588 static void test(string
str, Duration utcOffset
, size_t line
= __LINE__
)
1590 if (SimpleTimeZone
.fromISOString(str).utcOffset
!= (new immutable SimpleTimeZone(utcOffset
)).utcOffset
)
1591 throw new AssertError("unittest failure", __FILE__
, line
);
1594 test("+0000", Duration
.zero
);
1595 test("+0001", minutes(1));
1596 test("+0010", minutes(10));
1597 test("+0059", minutes(59));
1598 test("+0100", hours(1));
1599 test("+0130", hours(1) + minutes(30));
1600 test("+0200", hours(2));
1601 test("+0800", hours(8));
1602 test("+2359", hours(23) + minutes(59));
1604 test("-0001", minutes(-1));
1605 test("-0010", minutes(-10));
1606 test("-0059", minutes(-59));
1607 test("-0100", hours(-1));
1608 test("-0130", hours(-1) - minutes(30));
1609 test("-0200", hours(-2));
1610 test("-0800", hours(-8));
1611 test("-2359", hours(-23) - minutes(59));
1613 test("+00", Duration
.zero
);
1614 test("+01", hours(1));
1615 test("+02", hours(2));
1616 test("+12", hours(12));
1617 test("+23", hours(23));
1619 test("-00", Duration
.zero
);
1620 test("-01", hours(-1));
1621 test("-02", hours(-2));
1622 test("-12", hours(-12));
1623 test("-23", hours(-23));
1628 import core
.exception
: AssertError
;
1629 import std
.format
: format
;
1631 static void test(in string isoString
, int expectedOffset
, size_t line
= __LINE__
)
1633 auto stz
= SimpleTimeZone
.fromISOExtString(isoString
);
1634 if (stz
.utcOffset
!= dur
!"minutes"(expectedOffset
))
1635 throw new AssertError(format("unittest failure: wrong offset [%s]", stz
.utcOffset
), __FILE__
, line
);
1637 auto result
= SimpleTimeZone
.toISOExtString(stz
.utcOffset
);
1638 if (result
!= isoString
)
1639 throw new AssertError(format("unittest failure: [%s] != [%s]", result
, isoString
), __FILE__
, line
);
1648 test("+02:00", 120);
1649 test("+08:00", 480);
1650 test("+08:00", 480);
1651 test("+23:59", 1439);
1654 test("-00:10", -10);
1655 test("-00:59", -59);
1656 test("-01:00", -60);
1657 test("-01:30", -90);
1658 test("-02:00", -120);
1659 test("-08:00", -480);
1660 test("-08:00", -480);
1661 test("-23:59", -1439);
1666 Takes a time zone as a string with an offset from UTC and returns a
1667 $(LREF SimpleTimeZone) which matches.
1669 The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and
1673 isoExtString = A string which represents a time zone in the ISO format.
1675 static immutable(SimpleTimeZone
) fromISOExtString(S
)(S isoExtString
) @safe pure
1678 import std
.algorithm
.searching
: startsWith
, countUntil
, all
;
1679 import std
.ascii
: isDigit
;
1680 import std
.conv
: to
;
1681 import std
.format
: format
;
1683 auto dstr
= to
!dstring(isoExtString
);
1685 enforce
!DateTimeException(dstr
.startsWith('-', '+'), "Invalid ISO String");
1687 auto sign
= dstr
.startsWith('-') ?
-1 : 1;
1690 enforce
!DateTimeException(!dstr
.empty
, "Invalid ISO String");
1692 immutable colon
= dstr
.countUntil(':');
1699 hoursStr
= dstr
[0 .. colon
];
1700 minutesStr
= dstr
[colon
+ 1 .. $];
1701 enforce
!DateTimeException(minutesStr
.length
== 2, format("Invalid ISO String: %s", dstr
));
1706 enforce
!DateTimeException(hoursStr
.length
== 2, format("Invalid ISO String: %s", dstr
));
1707 enforce
!DateTimeException(all
!isDigit(hoursStr
), format("Invalid ISO String: %s", dstr
));
1708 enforce
!DateTimeException(all
!isDigit(minutesStr
), format("Invalid ISO String: %s", dstr
));
1710 immutable hours
= to
!int(hoursStr
);
1711 immutable minutes
= minutesStr
.empty ?
0 : to
!int(minutesStr
);
1712 enforce
!DateTimeException(hours
< 24 && minutes
< 60, format("Invalid ISO String: %s", dstr
));
1714 return new immutable SimpleTimeZone(sign
* (dur
!"hours"(hours
) + dur
!"minutes"(minutes
)));
1719 import core
.exception
: AssertError
;
1720 import std
.format
: format
;
1722 foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1",
1723 "-24:00", "+24:00", "-24", "+24", "-2400", "-2400",
1724 "1", "+1", "-1", "+9", "-9",
1725 "+1:0", "+01:0", "+1:00", "+01:000", "+01:60",
1726 "-1:0", "-01:0", "-1:00", "-01:000", "-01:60",
1727 "000", "00000", "0160", "-0160",
1728 " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ",
1729 " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ",
1730 " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ",
1731 " -0800", "- 0800", "-08 00", "-08 00", "-0800 ",
1732 "+ab:cd", "abcd", "+0Z:00", "+Z", "+00Z",
1733 "-ab:cd", "abcd", "-0Z:00", "-Z", "-00Z",
1734 "0100", "1200", "2359"])
1736 assertThrown
!DateTimeException(SimpleTimeZone
.fromISOExtString(str), format("[%s]", str));
1739 static void test(string
str, Duration utcOffset
, size_t line
= __LINE__
)
1741 if (SimpleTimeZone
.fromISOExtString(str).utcOffset
!= (new immutable SimpleTimeZone(utcOffset
)).utcOffset
)
1742 throw new AssertError("unittest failure", __FILE__
, line
);
1745 test("+00:00", Duration
.zero
);
1746 test("+00:01", minutes(1));
1747 test("+00:10", minutes(10));
1748 test("+00:59", minutes(59));
1749 test("+01:00", hours(1));
1750 test("+01:30", hours(1) + minutes(30));
1751 test("+02:00", hours(2));
1752 test("+08:00", hours(8));
1753 test("+23:59", hours(23) + minutes(59));
1755 test("-00:01", minutes(-1));
1756 test("-00:10", minutes(-10));
1757 test("-00:59", minutes(-59));
1758 test("-01:00", hours(-1));
1759 test("-01:30", hours(-1) - minutes(30));
1760 test("-02:00", hours(-2));
1761 test("-08:00", hours(-8));
1762 test("-23:59", hours(-23) - minutes(59));
1764 test("+00", Duration
.zero
);
1765 test("+01", hours(1));
1766 test("+02", hours(2));
1767 test("+12", hours(12));
1768 test("+23", hours(23));
1770 test("-00", Duration
.zero
);
1771 test("-01", hours(-1));
1772 test("-02", hours(-2));
1773 test("-12", hours(-12));
1774 test("-23", hours(-23));
1779 import core
.exception
: AssertError
;
1780 import std
.format
: format
;
1782 static void test(in string isoExtString
, int expectedOffset
, size_t line
= __LINE__
)
1784 auto stz
= SimpleTimeZone
.fromISOExtString(isoExtString
);
1785 if (stz
.utcOffset
!= dur
!"minutes"(expectedOffset
))
1786 throw new AssertError(format("unittest failure: wrong offset [%s]", stz
.utcOffset
), __FILE__
, line
);
1788 auto result
= SimpleTimeZone
.toISOExtString(stz
.utcOffset
);
1789 if (result
!= isoExtString
)
1790 throw new AssertError(format("unittest failure: [%s] != [%s]", result
, isoExtString
), __FILE__
, line
);
1799 test("+02:00", 120);
1800 test("+08:00", 480);
1801 test("+08:00", 480);
1802 test("+23:59", 1439);
1805 test("-00:10", -10);
1806 test("-00:59", -59);
1807 test("-01:00", -60);
1808 test("-01:30", -90);
1809 test("-02:00", -120);
1810 test("-08:00", -480);
1811 test("-08:00", -480);
1812 test("-23:59", -1439);
1818 immutable Duration _utcOffset
;
1823 Represents a time zone from a TZ Database time zone file. Files from the TZ
1824 Database are how Posix systems hold their time zone information.
1825 Unfortunately, Windows does not use the TZ Database. To use the TZ Database,
1826 use $(D PosixTimeZone) (which reads its information from the TZ Database
1827 files on disk) on Windows by providing the TZ Database files and telling
1828 $(D PosixTimeZone.getTimeZone) where the directory holding them is.
1830 To get a $(D PosixTimeZone), either call $(D PosixTimeZone.getTimeZone)
1831 (which allows specifying the location the time zone files) or call
1832 $(D TimeZone.getTimeZone) (which will give a $(D PosixTimeZone) on Posix
1833 systems and a $(LREF WindowsTimeZone) on Windows systems).
1836 Unless your system's local time zone deals with leap seconds (which is
1837 highly unlikely), then the only way to get a time zone which
1838 takes leap seconds into account is to use $(D PosixTimeZone) with a
1839 time zone whose name starts with "right/". Those time zone files do
1840 include leap seconds, and $(D PosixTimeZone) will take them into account
1841 (though posix systems which use a "right/" time zone as their local time
1842 zone will $(I not) take leap seconds into account even though they're
1846 $(HTTP www.iana.org/time-zones, Home of the TZ Database files)<br>
1847 $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)<br>
1848 $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time
1851 final class PosixTimeZone
: TimeZone
1853 import std
.algorithm
.searching
: countUntil
, canFind
, startsWith
;
1854 import std
.file
: isDir
, isFile
, exists
, dirEntries
, SpanMode
, DirEntry
;
1855 import std
.path
: extension
;
1856 import std
.stdio
: File
;
1857 import std
.string
: strip
, representation
;
1858 import std
.traits
: isArray
, isSomeChar
;
1862 Whether this time zone has Daylight Savings Time at any point in time.
1863 Note that for some time zone types it may not have DST for current
1864 dates but will still return true for $(D hasDST) because the time zone
1865 did at some point have DST.
1867 @property override bool hasDST() @safe const nothrow
1874 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
1875 in UTC time (i.e. std time) and returns whether DST is in effect in this
1876 time zone at the given point in time.
1879 stdTime = The UTC time that needs to be checked for DST in this time
1882 override bool dstInEffect(long stdTime
) @safe const nothrow
1884 assert(!_transitions
.empty
);
1886 immutable unixTime
= stdTimeToUnixTime(stdTime
);
1887 immutable found
= countUntil
!"b < a.timeT"(_transitions
, unixTime
);
1890 return _transitions
.back
.ttInfo
.isDST
;
1892 immutable transition
= found
== 0 ? _transitions
[0] : _transitions
[found
- 1];
1894 return transition
.ttInfo
.isDST
;
1899 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
1900 in UTC time (i.e. std time) and converts it to this time zone's time.
1903 stdTime = The UTC time that needs to be adjusted to this time zone's
1906 override long utcToTZ(long stdTime
) @safe const nothrow
1908 assert(!_transitions
.empty
);
1910 immutable leapSecs
= calculateLeapSeconds(stdTime
);
1911 immutable unixTime
= stdTimeToUnixTime(stdTime
);
1912 immutable found
= countUntil
!"b < a.timeT"(_transitions
, unixTime
);
1915 return stdTime
+ convert
!("seconds", "hnsecs")(_transitions
.back
.ttInfo
.utcOffset
+ leapSecs
);
1917 immutable transition
= found
== 0 ? _transitions
[0] : _transitions
[found
- 1];
1919 return stdTime
+ convert
!("seconds", "hnsecs")(transition
.ttInfo
.utcOffset
+ leapSecs
);
1924 Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.
1925 in this time zone's time and converts it to UTC (i.e. std time).
1928 adjTime = The time in this time zone that needs to be adjusted to
1931 override long tzToUTC(long adjTime
) @safe const nothrow
1933 assert(!_transitions
.empty
);
1935 immutable leapSecs
= calculateLeapSeconds(adjTime
);
1936 time_t unixTime
= stdTimeToUnixTime(adjTime
);
1937 immutable past
= unixTime
- convert
!("days", "seconds")(1);
1938 immutable future
= unixTime
+ convert
!("days", "seconds")(1);
1940 immutable pastFound
= countUntil
!"b < a.timeT"(_transitions
, past
);
1942 if (pastFound
== -1)
1943 return adjTime
- convert
!("seconds", "hnsecs")(_transitions
.back
.ttInfo
.utcOffset
+ leapSecs
);
1945 immutable futureFound
= countUntil
!"b < a.timeT"(_transitions
[pastFound
.. $], future
);
1946 immutable pastTrans
= pastFound
== 0 ? _transitions
[0] : _transitions
[pastFound
- 1];
1948 if (futureFound
== 0)
1949 return adjTime
- convert
!("seconds", "hnsecs")(pastTrans
.ttInfo
.utcOffset
+ leapSecs
);
1951 immutable futureTrans
= futureFound
== -1 ? _transitions
.back
1952 : _transitions
[pastFound
+ futureFound
- 1];
1953 immutable pastOffset
= pastTrans
.ttInfo
.utcOffset
;
1955 if (pastOffset
< futureTrans
.ttInfo
.utcOffset
)
1956 unixTime
-= convert
!("hours", "seconds")(1);
1958 immutable found
= countUntil
!"b < a.timeT"(_transitions
[pastFound
.. $], unixTime
- pastOffset
);
1961 return adjTime
- convert
!("seconds", "hnsecs")(_transitions
.back
.ttInfo
.utcOffset
+ leapSecs
);
1963 immutable transition
= found
== 0 ? pastTrans
: _transitions
[pastFound
+ found
- 1];
1965 return adjTime
- convert
!("seconds", "hnsecs")(transition
.ttInfo
.utcOffset
+ leapSecs
);
1971 // Android concatenates all time zone data into a single file and stores it here.
1972 enum defaultTZDatabaseDir
= "/system/usr/share/zoneinfo/";
1974 else version (Posix
)
1977 The default directory where the TZ Database files are. It's empty
1978 for Windows, since Windows doesn't have them.
1980 enum defaultTZDatabaseDir
= "/usr/share/zoneinfo/";
1982 else version (Windows
)
1984 /++ The default directory where the TZ Database files are. It's empty
1985 for Windows, since Windows doesn't have them.
1987 enum defaultTZDatabaseDir
= "";
1992 Returns a $(LREF TimeZone) with the give name per the TZ Database. The
1993 time zone information is fetched from the TZ Database time zone files in
1994 the given directory.
1997 $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
1999 $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of
2003 name = The TZ Database name of the desired time zone
2004 tzDatabaseDir = The directory where the TZ Database files are
2005 located. Because these files are not located on
2006 Windows systems, provide them
2007 and give their location here to
2008 use $(LREF PosixTimeZone)s.
2011 $(REF DateTimeException,std,datetime,date) if the given time zone
2012 could not be found or $(D FileException) if the TZ Database file
2013 could not be opened.
2015 // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed
2017 static immutable(PosixTimeZone
) getTimeZone(string name
, string tzDatabaseDir
= defaultTZDatabaseDir
) @trusted
2019 import std
.algorithm
.sorting
: sort
;
2020 import std
.conv
: to
;
2021 import std
.format
: format
;
2022 import std
.path
: asNormalizedPath
, chainPath
;
2023 import std
.range
: retro
;
2027 enforce(tzDatabaseDir
.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir
)));
2028 enforce(tzDatabaseDir
.isDir
, new DateTimeException(format("%s is not a directory.", tzDatabaseDir
)));
2032 auto tzfileOffset
= name
in tzdataIndex(tzDatabaseDir
);
2033 enforce(tzfileOffset
, new DateTimeException(format("The time zone %s is not listed.", name
)));
2034 string tzFilename
= separate_index ?
"zoneinfo.dat" : "tzdata";
2035 const file
= asNormalizedPath(chainPath(tzDatabaseDir
, tzFilename
)).to
!string
;
2038 const file
= asNormalizedPath(chainPath(tzDatabaseDir
, name
)).to
!string
;
2040 enforce(file
.exists(), new DateTimeException(format("File %s does not exist.", file
)));
2041 enforce(file
.isFile
, new DateTimeException(format("%s is not a file.", file
)));
2043 auto tzFile
= File(file
);
2044 version (Android
) tzFile
.seek(*tzfileOffset
);
2045 immutable gmtZone
= name
.representation().canFind("GMT");
2049 _enforceValidTZFile(readVal
!(char[])(tzFile
, 4) == "TZif");
2051 immutable char tzFileVersion
= readVal
!char(tzFile
);
2052 _enforceValidTZFile(tzFileVersion
== '\0' || tzFileVersion
== '2' || tzFileVersion
== '3');
2055 auto zeroBlock
= readVal
!(ubyte[])(tzFile
, 15);
2056 bool allZeroes
= true;
2058 foreach (val
; zeroBlock
)
2067 _enforceValidTZFile(allZeroes
);
2071 // The number of UTC/local indicators stored in the file.
2072 auto tzh_ttisgmtcnt
= readVal
!int(tzFile
);
2074 // The number of standard/wall indicators stored in the file.
2075 auto tzh_ttisstdcnt
= readVal
!int(tzFile
);
2077 // The number of leap seconds for which data is stored in the file.
2078 auto tzh_leapcnt
= readVal
!int(tzFile
);
2080 // The number of "transition times" for which data is stored in the file.
2081 auto tzh_timecnt
= readVal
!int(tzFile
);
2083 // The number of "local time types" for which data is stored in the file (must not be zero).
2084 auto tzh_typecnt
= readVal
!int(tzFile
);
2085 _enforceValidTZFile(tzh_typecnt
!= 0);
2087 // The number of characters of "timezone abbreviation strings" stored in the file.
2088 auto tzh_charcnt
= readVal
!int(tzFile
);
2090 // time_ts where DST transitions occur.
2091 auto transitionTimeTs
= new long[](tzh_timecnt
);
2092 foreach (ref transition
; transitionTimeTs
)
2093 transition
= readVal
!int(tzFile
);
2095 // Indices into ttinfo structs indicating the changes
2096 // to be made at the corresponding DST transition.
2097 auto ttInfoIndices
= new ubyte[](tzh_timecnt
);
2098 foreach (ref ttInfoIndex
; ttInfoIndices
)
2099 ttInfoIndex
= readVal
!ubyte(tzFile
);
2101 // ttinfos which give info on DST transitions.
2102 auto tempTTInfos
= new TempTTInfo
[](tzh_typecnt
);
2103 foreach (ref ttInfo
; tempTTInfos
)
2104 ttInfo
= readVal
!TempTTInfo(tzFile
);
2106 // The array of time zone abbreviation characters.
2107 auto tzAbbrevChars
= readVal
!(char[])(tzFile
, tzh_charcnt
);
2109 auto leapSeconds
= new LeapSecond
[](tzh_leapcnt
);
2110 foreach (ref leapSecond
; leapSeconds
)
2112 // The time_t when the leap second occurs.
2113 auto timeT
= readVal
!int(tzFile
);
2115 // The total number of leap seconds to be applied after
2116 // the corresponding leap second.
2117 auto total
= readVal
!int(tzFile
);
2119 leapSecond
= LeapSecond(timeT
, total
);
2122 // Indicate whether each corresponding DST transition were specified
2123 // in standard time or wall clock time.
2124 auto transitionIsStd
= new bool[](tzh_ttisstdcnt
);
2125 foreach (ref isStd
; transitionIsStd
)
2126 isStd
= readVal
!bool(tzFile
);
2128 // Indicate whether each corresponding DST transition associated with
2129 // local time types are specified in UTC or local time.
2130 auto transitionInUTC
= new bool[](tzh_ttisgmtcnt
);
2131 foreach (ref inUTC
; transitionInUTC
)
2132 inUTC
= readVal
!bool(tzFile
);
2134 _enforceValidTZFile(!tzFile
.eof
);
2136 // If version 2 or 3, the information is duplicated in 64-bit.
2137 if (tzFileVersion
== '2' || tzFileVersion
== '3')
2139 _enforceValidTZFile(readVal
!(char[])(tzFile
, 4) == "TZif");
2141 immutable char tzFileVersion2
= readVal
!(char)(tzFile
);
2142 _enforceValidTZFile(tzFileVersion2
== '2' || tzFileVersion2
== '3');
2145 auto zeroBlock
= readVal
!(ubyte[])(tzFile
, 15);
2146 bool allZeroes
= true;
2148 foreach (val
; zeroBlock
)
2157 _enforceValidTZFile(allZeroes
);
2161 // The number of UTC/local indicators stored in the file.
2162 tzh_ttisgmtcnt
= readVal
!int(tzFile
);
2164 // The number of standard/wall indicators stored in the file.
2165 tzh_ttisstdcnt
= readVal
!int(tzFile
);
2167 // The number of leap seconds for which data is stored in the file.
2168 tzh_leapcnt
= readVal
!int(tzFile
);
2170 // The number of "transition times" for which data is stored in the file.
2171 tzh_timecnt
= readVal
!int(tzFile
);
2173 // The number of "local time types" for which data is stored in the file (must not be zero).
2174 tzh_typecnt
= readVal
!int(tzFile
);
2175 _enforceValidTZFile(tzh_typecnt
!= 0);
2177 // The number of characters of "timezone abbreviation strings" stored in the file.
2178 tzh_charcnt
= readVal
!int(tzFile
);
2180 // time_ts where DST transitions occur.
2181 transitionTimeTs
= new long[](tzh_timecnt
);
2182 foreach (ref transition
; transitionTimeTs
)
2183 transition
= readVal
!long(tzFile
);
2185 // Indices into ttinfo structs indicating the changes
2186 // to be made at the corresponding DST transition.
2187 ttInfoIndices
= new ubyte[](tzh_timecnt
);
2188 foreach (ref ttInfoIndex
; ttInfoIndices
)
2189 ttInfoIndex
= readVal
!ubyte(tzFile
);
2191 // ttinfos which give info on DST transitions.
2192 tempTTInfos
= new TempTTInfo
[](tzh_typecnt
);
2193 foreach (ref ttInfo
; tempTTInfos
)
2194 ttInfo
= readVal
!TempTTInfo(tzFile
);
2196 // The array of time zone abbreviation characters.
2197 tzAbbrevChars
= readVal
!(char[])(tzFile
, tzh_charcnt
);
2199 leapSeconds
= new LeapSecond
[](tzh_leapcnt
);
2200 foreach (ref leapSecond
; leapSeconds
)
2202 // The time_t when the leap second occurs.
2203 auto timeT
= readVal
!long(tzFile
);
2205 // The total number of leap seconds to be applied after
2206 // the corresponding leap second.
2207 auto total
= readVal
!int(tzFile
);
2209 leapSecond
= LeapSecond(timeT
, total
);
2212 // Indicate whether each corresponding DST transition were specified
2213 // in standard time or wall clock time.
2214 transitionIsStd
= new bool[](tzh_ttisstdcnt
);
2215 foreach (ref isStd
; transitionIsStd
)
2216 isStd
= readVal
!bool(tzFile
);
2218 // Indicate whether each corresponding DST transition associated with
2219 // local time types are specified in UTC or local time.
2220 transitionInUTC
= new bool[](tzh_ttisgmtcnt
);
2221 foreach (ref inUTC
; transitionInUTC
)
2222 inUTC
= readVal
!bool(tzFile
);
2225 _enforceValidTZFile(tzFile
.readln().strip().empty
);
2227 cast(void) tzFile
.readln();
2231 // Android uses a single file for all timezone data, so the file
2232 // doesn't end here.
2236 _enforceValidTZFile(tzFile
.readln().strip().empty
);
2237 _enforceValidTZFile(tzFile
.eof
);
2241 auto transitionTypes
= new TransitionType
*[](tempTTInfos
.length
);
2243 foreach (i
, ref ttype
; transitionTypes
)
2247 if (i
< transitionIsStd
.length
&& !transitionIsStd
.empty
)
2248 isStd
= transitionIsStd
[i
];
2252 if (i
< transitionInUTC
.length
&& !transitionInUTC
.empty
)
2253 inUTC
= transitionInUTC
[i
];
2255 ttype
= new TransitionType(isStd
, inUTC
);
2258 auto ttInfos
= new immutable(TTInfo
)*[](tempTTInfos
.length
);
2259 foreach (i
, ref ttInfo
; ttInfos
)
2261 auto tempTTInfo
= tempTTInfos
[i
];
2264 tempTTInfo
.tt_gmtoff
= -tempTTInfo
.tt_gmtoff
;
2266 auto abbrevChars
= tzAbbrevChars
[tempTTInfo
.tt_abbrind
.. $];
2267 string abbrev
= abbrevChars
[0 .. abbrevChars
.countUntil('\0')].idup
;
2269 ttInfo
= new immutable(TTInfo
)(tempTTInfos
[i
], abbrev
);
2272 auto tempTransitions
= new TempTransition
[](transitionTimeTs
.length
);
2273 foreach (i
, ref tempTransition
; tempTransitions
)
2275 immutable ttiIndex
= ttInfoIndices
[i
];
2276 auto transitionTimeT
= transitionTimeTs
[i
];
2277 auto ttype
= transitionTypes
[ttiIndex
];
2278 auto ttInfo
= ttInfos
[ttiIndex
];
2280 tempTransition
= TempTransition(transitionTimeT
, ttInfo
, ttype
);
2283 if (tempTransitions
.empty
)
2285 _enforceValidTZFile(ttInfos
.length
== 1 && transitionTypes
.length
== 1);
2286 tempTransitions
~= TempTransition(0, ttInfos
[0], transitionTypes
[0]);
2289 sort
!"a.timeT < b.timeT"(tempTransitions
);
2290 sort
!"a.timeT < b.timeT"(leapSeconds
);
2292 auto transitions
= new Transition
[](tempTransitions
.length
);
2293 foreach (i
, ref transition
; transitions
)
2295 auto tempTransition
= tempTransitions
[i
];
2296 auto transitionTimeT
= tempTransition
.timeT
;
2297 auto ttInfo
= tempTransition
.ttInfo
;
2299 _enforceValidTZFile(i
== 0 || transitionTimeT
> tempTransitions
[i
- 1].timeT
);
2301 transition
= Transition(transitionTimeT
, ttInfo
);
2306 bool hasDST
= false;
2308 foreach (transition
; retro(transitions
))
2310 auto ttInfo
= transition
.ttInfo
;
2315 dstName
= ttInfo
.abbrev
;
2321 stdName
= ttInfo
.abbrev
;
2324 if (!stdName
.empty
&& !dstName
.empty
)
2328 return new immutable PosixTimeZone(transitions
.idup
, leapSeconds
.idup
, name
, stdName
, dstName
, hasDST
);
2330 catch (DateTimeException dte
)
2333 throw new DateTimeException("Not a valid TZ data file", __FILE__
, __LINE__
, e
);
2341 auto tz
= PosixTimeZone
.getTimeZone("America/Los_Angeles");
2343 assert(tz
.name
== "America/Los_Angeles");
2344 assert(tz
.stdName
== "PST");
2345 assert(tz
.dstName
== "PDT");
2350 Returns a list of the names of the time zones installed on the system.
2352 Providing a sub-name narrows down the list of time zones (which
2353 can number in the thousands). For example,
2354 passing in "America" as the sub-name returns only the time zones which
2355 begin with "America".
2358 subName = The first part of the desired time zones.
2359 tzDatabaseDir = The directory where the TZ Database files are
2363 $(D FileException) if it fails to read from disk.
2365 static string
[] getInstalledTZNames(string subName
= "", string tzDatabaseDir
= defaultTZDatabaseDir
) @trusted
2367 import std
.algorithm
.sorting
: sort
;
2368 import std
.array
: appender
;
2369 import std
.format
: format
;
2372 subName
= strip(subName
);
2373 else version (Windows
)
2375 import std
.array
: replace
;
2376 import std
.path
: dirSeparator
;
2377 subName
= replace(strip(subName
), "/", dirSeparator
);
2380 enforce(tzDatabaseDir
.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir
)));
2381 enforce(tzDatabaseDir
.isDir
, new DateTimeException(format("%s is not a directory.", tzDatabaseDir
)));
2383 auto timezones
= appender
!(string
[])();
2387 import std
.algorithm
.iteration
: filter
;
2388 import std
.algorithm
.mutation
: copy
;
2389 tzdataIndex(tzDatabaseDir
).byKey
.filter
!(a
=> a
.startsWith(subName
)).copy(timezones
);
2393 foreach (DirEntry
de; dirEntries(tzDatabaseDir
, SpanMode
.depth
))
2397 auto tzName
= de.name
[tzDatabaseDir
.length
.. $];
2399 if (!tzName
.extension().empty ||
2400 !tzName
.startsWith(subName
) ||
2401 tzName
== "leapseconds" ||
2402 tzName
== "+VERSION")
2407 timezones
.put(tzName
);
2412 sort(timezones
.data
);
2414 return timezones
.data
;
2417 version (Posix
) @system unittest
2419 import std
.exception
: assertNotThrown
;
2420 import std
.stdio
: writefln
;
2421 static void testPTZSuccess(string tzName
)
2423 scope(failure
) writefln("TZName which threw: %s", tzName
);
2425 PosixTimeZone
.getTimeZone(tzName
);
2428 static void testPTZFailure(string tzName
)
2430 scope(success
) writefln("TZName which was supposed to throw: %s", tzName
);
2432 PosixTimeZone
.getTimeZone(tzName
);
2435 auto tzNames
= getInstalledTZNames();
2437 foreach (tzName
; tzNames
)
2438 assertNotThrown
!DateTimeException(testPTZSuccess(tzName
));
2440 // No timezone directories on Android, just a single tzdata file
2445 foreach (DirEntry
de; dirEntries(defaultTZDatabaseDir
, SpanMode
.depth
))
2449 auto tzName
= de.name
[defaultTZDatabaseDir
.length
.. $];
2451 if (!canFind(tzNames
, tzName
))
2452 assertThrown
!DateTimeException(testPTZFailure(tzName
));
2462 Holds information on when a time transition occures (usually a
2463 transition to or from DST) as well as a pointer to the $(D TTInfo) which
2464 holds information on the utc offset past the transition.
2468 this(long timeT
, immutable (TTInfo
)* ttInfo
) @safe pure
2471 this.ttInfo
= ttInfo
;
2475 immutable (TTInfo
)* ttInfo
;
2480 Holds information on when a leap second occurs.
2484 this(long timeT
, int total
) @safe pure
2495 Holds information on the utc offset after a transition as well as
2496 whether DST is in effect after that transition.
2500 this(in TempTTInfo tempTTInfo
, string abbrev
) @safe immutable pure
2502 utcOffset
= tempTTInfo
.tt_gmtoff
;
2503 isDST
= tempTTInfo
.tt_isdst
;
2504 this.abbrev
= abbrev
;
2507 immutable int utcOffset
; // Offset from UTC.
2508 immutable bool isDST
; // Whether DST is in effect.
2509 immutable string abbrev
; // The current abbreviation for the time zone.
2514 Struct used to hold information relating to $(D TTInfo) while organizing
2515 the time zone information prior to putting it in its final form.
2519 this(int gmtOff
, bool isDST
, ubyte abbrInd
) @safe pure
2523 tt_abbrind
= abbrInd
;
2533 Struct used to hold information relating to $(D Transition) while
2534 organizing the time zone information prior to putting it in its final
2537 struct TempTransition
2539 this(long timeT
, immutable (TTInfo
)* ttInfo
, TransitionType
* ttype
) @safe pure
2542 this.ttInfo
= ttInfo
;
2547 immutable (TTInfo
)* ttInfo
;
2548 TransitionType
* ttype
;
2553 Struct used to hold information relating to $(D Transition) and
2554 $(D TTInfo) while organizing the time zone information prior to putting
2555 it in its final form.
2557 struct TransitionType
2559 this(bool isStd
, bool inUTC
) @safe pure
2565 // Whether the transition is in std time (as opposed to wall clock time).
2568 // Whether the transition is in UTC (as opposed to local time).
2574 Reads an int from a TZ file.
2576 static T
readVal(T
)(ref File tzFile
) @trusted
2577 if ((isIntegral
!T || isSomeChar
!T
) ||
is(Unqual
!T
== bool))
2579 import std
.bitmanip
: bigEndianToNative
;
2582 _enforceValidTZFile(!tzFile
.eof
);
2583 tzFile
.rawRead(buff
);
2585 return bigEndianToNative
!T(cast(ubyte[T
.sizeof
]) buff
);
2589 Reads an array of values from a TZ file.
2591 static T
readVal(T
)(ref File tzFile
, size_t length
) @trusted
2594 auto buff
= new T(length
);
2596 _enforceValidTZFile(!tzFile
.eof
);
2597 tzFile
.rawRead(buff
);
2604 Reads a $(D TempTTInfo) from a TZ file.
2606 static T
readVal(T
)(ref File tzFile
) @safe
2607 if (is(T
== TempTTInfo
))
2609 return TempTTInfo(readVal
!int(tzFile
),
2610 readVal
!bool(tzFile
),
2611 readVal
!ubyte(tzFile
));
2617 $(REF DateTimeException,std,datetime,date) if $(D result) is false.
2619 static void _enforceValidTZFile(bool result
, size_t line
= __LINE__
) @safe pure
2622 throw new DateTimeException("Not a valid tzdata file.", __FILE__
, line
);
2626 int calculateLeapSeconds(long stdTime
) @safe const pure nothrow
2628 if (_leapSeconds
.empty
)
2631 immutable unixTime
= stdTimeToUnixTime(stdTime
);
2633 if (_leapSeconds
.front
.timeT
>= unixTime
)
2636 immutable found
= countUntil
!"b < a.timeT"(_leapSeconds
, unixTime
);
2639 return _leapSeconds
.back
.total
;
2641 immutable leapSecond
= found
== 0 ? _leapSeconds
[0] : _leapSeconds
[found
- 1];
2643 return leapSecond
.total
;
2647 this(immutable Transition
[] transitions
,
2648 immutable LeapSecond
[] leapSeconds
,
2652 bool hasDST
) @safe immutable pure
2654 if (dstName
.empty
&& !stdName
.empty
)
2656 else if (stdName
.empty
&& !dstName
.empty
)
2659 super(name
, stdName
, dstName
);
2661 if (!transitions
.empty
)
2663 foreach (i
, transition
; transitions
[0 .. $-1])
2664 _enforceValidTZFile(transition
.timeT
< transitions
[i
+ 1].timeT
);
2667 foreach (i
, leapSecond
; leapSeconds
)
2668 _enforceValidTZFile(i
== leapSeconds
.length
- 1 || leapSecond
.timeT
< leapSeconds
[i
+ 1].timeT
);
2670 _transitions
= transitions
;
2671 _leapSeconds
= leapSeconds
;
2675 // Android concatenates the usual timezone directories into a single file,
2676 // tzdata, along with an index to jump to each timezone's offset. In older
2677 // versions of Android, the index was stored in a separate file, zoneinfo.idx,
2678 // whereas now it's stored at the beginning of tzdata.
2681 // Keep track of whether there's a separate index, zoneinfo.idx. Only
2682 // check this after calling tzdataIndex, as it's initialized there.
2683 static shared bool separate_index
;
2685 // Extracts the name of each time zone and the offset where its data is
2686 // located in the tzdata file from the index and caches it for later.
2687 static const(uint[string
]) tzdataIndex(string tzDir
)
2689 import std
.concurrency
: initOnce
;
2691 static __gshared
uint[string
] _tzIndex
;
2693 // _tzIndex is initialized once and then shared across all threads.
2696 import std
.conv
: to
;
2697 import std
.format
: format
;
2698 import std
.path
: asNormalizedPath
, chainPath
;
2700 enum indexEntrySize
= 52;
2701 const combinedFile
= asNormalizedPath(chainPath(tzDir
, "tzdata")).to
!string
;
2702 const indexFile
= asNormalizedPath(chainPath(tzDir
, "zoneinfo.idx")).to
!string
;
2704 uint indexEntries
, dataOffset
;
2705 uint[string
] initIndex
;
2707 // Check for the combined file tzdata, which stores the index
2708 // and the time zone data together.
2709 if (combinedFile
.exists() && combinedFile
.isFile
)
2711 tzFile
= File(combinedFile
);
2712 _enforceValidTZFile(readVal
!(char[])(tzFile
, 6) == "tzdata");
2713 auto tzDataVersion
= readVal
!(char[])(tzFile
, 6);
2714 _enforceValidTZFile(tzDataVersion
[5] == '\0');
2716 uint indexOffset
= readVal
!uint(tzFile
);
2717 dataOffset
= readVal
!uint(tzFile
);
2718 readVal
!uint(tzFile
);
2720 indexEntries
= (dataOffset
- indexOffset
) / indexEntrySize
;
2721 separate_index
= false;
2723 else if (indexFile
.exists() && indexFile
.isFile
)
2725 tzFile
= File(indexFile
);
2726 indexEntries
= to
!uint(tzFile
.size
/indexEntrySize
);
2727 separate_index
= true;
2731 throw new DateTimeException(format("Both timezone files %s and %s do not exist.",
2732 combinedFile
, indexFile
));
2735 foreach (_
; 0 .. indexEntries
)
2737 string tzName
= to
!string(readVal
!(char[])(tzFile
, 40).ptr
);
2738 uint tzOffset
= readVal
!uint(tzFile
);
2739 readVal
!(uint[])(tzFile
, 2);
2740 initIndex
[tzName
] = dataOffset
+ tzOffset
;
2749 // List of times when the utc offset changes.
2750 immutable Transition
[] _transitions
;
2752 // List of leap second occurrences.
2753 immutable LeapSecond
[] _leapSeconds
;
2755 // Whether DST is in effect for this time zone at any point in time.
2756 immutable bool _hasDST
;
2763 $(BLUE This class is Windows-Only.)
2765 Represents a time zone from the Windows registry. Unfortunately, Windows
2766 does not use the TZ Database. To use the TZ Database, use
2767 $(LREF PosixTimeZone) (which reads its information from the TZ Database
2768 files on disk) on Windows by providing the TZ Database files and telling
2769 $(D PosixTimeZone.getTimeZone) where the directory holding them is.
2771 The TZ Database files and Windows' time zone information frequently
2772 do not match. Windows has many errors with regards to when DST switches
2773 occur (especially for historical dates). Also, the TZ Database files
2774 include far more time zones than Windows does. So, for accurate
2775 time zone information, use the TZ Database files with
2776 $(LREF PosixTimeZone) rather than $(D WindowsTimeZone). However, because
2777 $(D WindowsTimeZone) uses Windows system calls to deal with the time,
2778 it's far more likely to match the behavior of other Windows programs.
2779 Be aware of the differences when selecting a method.
2781 $(D WindowsTimeZone) does not exist on Posix systems.
2783 To get a $(D WindowsTimeZone), either call
2784 $(D WindowsTimeZone.getTimeZone) or call $(D TimeZone.getTimeZone)
2785 (which will give a $(LREF PosixTimeZone) on Posix systems and a
2786 $(D WindowsTimeZone) on Windows systems).
2789 $(HTTP www.iana.org/time-zones, Home of the TZ Database files)
2791 final class WindowsTimeZone
: TimeZone
2796 Whether this time zone has Daylight Savings Time at any point in
2797 time. Note that for some time zone types it may not have DST for
2798 current dates but will still return true for $(D hasDST) because the
2799 time zone did at some point have DST.
2801 @property override bool hasDST() @safe const nothrow;
2805 Takes the number of hnsecs (100 ns) since midnight, January 1st,
2806 1 A.D. in UTC time (i.e. std time) and returns whether DST is in
2807 effect in this time zone at the given point in time.
2810 stdTime = The UTC time that needs to be checked for DST in this
2813 override bool dstInEffect(long stdTime
) @safe const nothrow;
2817 Takes the number of hnsecs (100 ns) since midnight, January 1st,
2818 1 A.D. in UTC time (i.e. std time) and converts it to this time
2822 stdTime = The UTC time that needs to be adjusted to this time
2825 override long utcToTZ(long stdTime
) @safe const nothrow;
2829 Takes the number of hnsecs (100 ns) since midnight, January 1st,
2830 1 A.D. in this time zone's time and converts it to UTC (i.e. std
2834 adjTime = The time in this time zone that needs to be adjusted
2837 override long tzToUTC(long adjTime
) @safe const nothrow;
2841 Returns a $(LREF TimeZone) with the given name per the Windows time
2842 zone names. The time zone information is fetched from the Windows
2846 $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ
2848 $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List
2852 name = The TZ Database name of the desired time zone.
2855 $(REF DateTimeException,std,datetime,date) if the given time
2856 zone could not be found.
2859 --------------------
2860 auto tz = WindowsTimeZone.getTimeZone("Pacific Standard Time");
2861 --------------------
2863 static immutable(WindowsTimeZone
) getTimeZone(string name
) @safe;
2867 Returns a list of the names of the time zones installed on the
2868 system. The list returned by WindowsTimeZone contains the Windows
2869 TZ names, not the TZ Database names. However,
2870 $(D TimeZone.getinstalledTZNames) will return the TZ Database names
2871 which are equivalent to the Windows TZ names.
2873 static string
[] getInstalledTZNames() @safe;
2880 alias TIME_ZONE_INFORMATION
= void*;
2882 static bool _dstInEffect(const TIME_ZONE_INFORMATION
* tzInfo
, long stdTime
) nothrow;
2883 static long _utcToTZ(const TIME_ZONE_INFORMATION
* tzInfo
, long stdTime
, bool hasDST
) nothrow;
2884 static long _tzToUTC(const TIME_ZONE_INFORMATION
* tzInfo
, long adjTime
, bool hasDST
) nothrow;
2886 this() immutable pure
2893 else version (Windows
)
2895 final class WindowsTimeZone
: TimeZone
2897 import std
.algorithm
.sorting
: sort
;
2898 import std
.array
: appender
;
2899 import std
.conv
: to
;
2900 import std
.format
: format
;
2904 @property override bool hasDST() @safe const nothrow
2906 return _tzInfo
.DaylightDate
.wMonth
!= 0;
2910 override bool dstInEffect(long stdTime
) @safe const nothrow
2912 return _dstInEffect(&_tzInfo
, stdTime
);
2916 override long utcToTZ(long stdTime
) @safe const nothrow
2918 return _utcToTZ(&_tzInfo
, stdTime
, hasDST
);
2922 override long tzToUTC(long adjTime
) @safe const nothrow
2924 return _tzToUTC(&_tzInfo
, adjTime
, hasDST
);
2928 static immutable(WindowsTimeZone
) getTimeZone(string name
) @trusted
2930 scope baseKey
= Registry
.localMachine
.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`);
2932 foreach (tzKeyName
; baseKey
.keyNames
)
2934 if (tzKeyName
!= name
)
2937 scope tzKey
= baseKey
.getKey(tzKeyName
);
2939 scope stdVal
= tzKey
.getValue("Std");
2940 auto stdName
= stdVal
.value_SZ
;
2942 scope dstVal
= tzKey
.getValue("Dlt");
2943 auto dstName
= dstVal
.value_SZ
;
2945 scope tziVal
= tzKey
.getValue("TZI");
2946 auto binVal
= tziVal
.value_BINARY
;
2947 assert(binVal
.length
== REG_TZI_FORMAT
.sizeof
);
2948 auto tziFmt
= cast(REG_TZI_FORMAT
*) binVal
.ptr
;
2950 TIME_ZONE_INFORMATION tzInfo
;
2952 auto wstdName
= stdName
.to
!wstring
;
2953 auto wdstName
= dstName
.to
!wstring
;
2954 auto wstdNameLen
= wstdName
.length
> 32 ?
32 : wstdName
.length
;
2955 auto wdstNameLen
= wdstName
.length
> 32 ?
32 : wdstName
.length
;
2957 tzInfo
.Bias
= tziFmt
.Bias
;
2958 tzInfo
.StandardName
[0 .. wstdNameLen
] = wstdName
[0 .. wstdNameLen
];
2959 tzInfo
.StandardName
[wstdNameLen
.. $] = '\0';
2960 tzInfo
.StandardDate
= tziFmt
.StandardDate
;
2961 tzInfo
.StandardBias
= tziFmt
.StandardBias
;
2962 tzInfo
.DaylightName
[0 .. wdstNameLen
] = wdstName
[0 .. wdstNameLen
];
2963 tzInfo
.DaylightName
[wdstNameLen
.. $] = '\0';
2964 tzInfo
.DaylightDate
= tziFmt
.DaylightDate
;
2965 tzInfo
.DaylightBias
= tziFmt
.DaylightBias
;
2967 return new immutable WindowsTimeZone(name
, tzInfo
);
2969 throw new DateTimeException(format("Failed to find time zone: %s", name
));
2972 static string
[] getInstalledTZNames() @trusted
2974 auto timezones
= appender
!(string
[])();
2976 scope baseKey
= Registry
.localMachine
.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`);
2978 foreach (tzKeyName
; baseKey
.keyNames
)
2979 timezones
.put(tzKeyName
);
2980 sort(timezones
.data
);
2982 return timezones
.data
;
2987 import std
.exception
: assertNotThrown
;
2988 import std
.stdio
: writefln
;
2989 static void testWTZSuccess(string tzName
)
2991 scope(failure
) writefln("TZName which threw: %s", tzName
);
2993 WindowsTimeZone
.getTimeZone(tzName
);
2996 auto tzNames
= getInstalledTZNames();
2998 foreach (tzName
; tzNames
)
2999 assertNotThrown
!DateTimeException(testWTZSuccess(tzName
));
3005 static bool _dstInEffect(const TIME_ZONE_INFORMATION
* tzInfo
, long stdTime
) @trusted nothrow
3009 if (tzInfo
.DaylightDate
.wMonth
== 0)
3012 auto utcDateTime
= cast(DateTime
) SysTime(stdTime
, UTC());
3014 //The limits of what SystemTimeToTzSpecificLocalTime will accept.
3015 if (utcDateTime
.year
< 1601)
3017 if (utcDateTime
.month
== Month
.feb
&& utcDateTime
.day
== 29)
3018 utcDateTime
.day
= 28;
3019 utcDateTime
.year
= 1601;
3021 else if (utcDateTime
.year
> 30_827)
3023 if (utcDateTime
.month
== Month
.feb
&& utcDateTime
.day
== 29)
3024 utcDateTime
.day
= 28;
3025 utcDateTime
.year
= 30_827;
3028 //SystemTimeToTzSpecificLocalTime doesn't act correctly at the
3029 //beginning or end of the year (bleh). Unless some bizarre time
3030 //zone changes DST on January 1st or December 31st, this should
3032 if (utcDateTime
.month
== Month
.jan
)
3034 if (utcDateTime
.day
== 1)
3035 utcDateTime
.day
= 2;
3037 else if (utcDateTime
.month
== Month
.dec && utcDateTime
.day
== 31)
3038 utcDateTime
.day
= 30;
3040 SYSTEMTIME utcTime
= void;
3041 SYSTEMTIME otherTime
= void;
3043 utcTime
.wYear
= utcDateTime
.year
;
3044 utcTime
.wMonth
= utcDateTime
.month
;
3045 utcTime
.wDay
= utcDateTime
.day
;
3046 utcTime
.wHour
= utcDateTime
.hour
;
3047 utcTime
.wMinute
= utcDateTime
.minute
;
3048 utcTime
.wSecond
= utcDateTime
.second
;
3049 utcTime
.wMilliseconds
= 0;
3051 immutable result
= SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION
*) tzInfo
,
3056 immutable otherDateTime
= DateTime(otherTime
.wYear
,
3062 immutable diff
= utcDateTime
- otherDateTime
;
3063 immutable minutes
= diff
.total
!"minutes" - tzInfo
.Bias
;
3065 if (minutes
== tzInfo
.DaylightBias
)
3068 assert(minutes
== tzInfo
.StandardBias
);
3073 assert(0, "DateTime's constructor threw.");
3078 TIME_ZONE_INFORMATION tzInfo
;
3079 GetTimeZoneInformation(&tzInfo
);
3081 foreach (year
; [1600, 1601, 30_827, 30_828])
3082 WindowsTimeZone
._dstInEffect(&tzInfo
, SysTime(DateTime(year
, 1, 1)).stdTime
);
3086 static long _utcToTZ(const TIME_ZONE_INFORMATION
* tzInfo
, long stdTime
, bool hasDST
) @safe nothrow
3088 if (hasDST
&& WindowsTimeZone
._dstInEffect(tzInfo
, stdTime
))
3089 return stdTime
- convert
!("minutes", "hnsecs")(tzInfo
.Bias
+ tzInfo
.DaylightBias
);
3091 return stdTime
- convert
!("minutes", "hnsecs")(tzInfo
.Bias
+ tzInfo
.StandardBias
);
3095 static long _tzToUTC(const TIME_ZONE_INFORMATION
* tzInfo
, long adjTime
, bool hasDST
) @trusted nothrow
3101 bool dstInEffectForLocalDateTime(DateTime localDateTime
)
3103 // The limits of what SystemTimeToTzSpecificLocalTime will accept.
3104 if (localDateTime
.year
< 1601)
3106 if (localDateTime
.month
== Month
.feb
&& localDateTime
.day
== 29)
3107 localDateTime
.day
= 28;
3109 localDateTime
.year
= 1601;
3111 else if (localDateTime
.year
> 30_827)
3113 if (localDateTime
.month
== Month
.feb
&& localDateTime
.day
== 29)
3114 localDateTime
.day
= 28;
3116 localDateTime
.year
= 30_827;
3119 // SystemTimeToTzSpecificLocalTime doesn't act correctly at the
3120 // beginning or end of the year (bleh). Unless some bizarre time
3121 // zone changes DST on January 1st or December 31st, this should
3123 if (localDateTime
.month
== Month
.jan
)
3125 if (localDateTime
.day
== 1)
3126 localDateTime
.day
= 2;
3128 else if (localDateTime
.month
== Month
.dec && localDateTime
.day
== 31)
3129 localDateTime
.day
= 30;
3131 SYSTEMTIME utcTime
= void;
3132 SYSTEMTIME localTime
= void;
3134 localTime
.wYear
= localDateTime
.year
;
3135 localTime
.wMonth
= localDateTime
.month
;
3136 localTime
.wDay
= localDateTime
.day
;
3137 localTime
.wHour
= localDateTime
.hour
;
3138 localTime
.wMinute
= localDateTime
.minute
;
3139 localTime
.wSecond
= localDateTime
.second
;
3140 localTime
.wMilliseconds
= 0;
3142 immutable result
= TzSpecificLocalTimeToSystemTime(cast(TIME_ZONE_INFORMATION
*) tzInfo
,
3147 immutable utcDateTime
= DateTime(utcTime
.wYear
,
3154 immutable diff
= localDateTime
- utcDateTime
;
3155 immutable minutes
= -tzInfo
.Bias
- diff
.total
!"minutes";
3157 if (minutes
== tzInfo
.DaylightBias
)
3160 assert(minutes
== tzInfo
.StandardBias
);
3165 auto localDateTime
= cast(DateTime
) SysTime(adjTime
, UTC());
3166 auto localDateTimeBefore
= localDateTime
- dur
!"hours"(1);
3167 auto localDateTimeAfter
= localDateTime
+ dur
!"hours"(1);
3169 auto dstInEffectNow
= dstInEffectForLocalDateTime(localDateTime
);
3170 auto dstInEffectBefore
= dstInEffectForLocalDateTime(localDateTimeBefore
);
3171 auto dstInEffectAfter
= dstInEffectForLocalDateTime(localDateTimeAfter
);
3175 if (dstInEffectBefore
&& dstInEffectNow
&& dstInEffectAfter
)
3177 else if (!dstInEffectBefore
&& !dstInEffectNow
&& !dstInEffectAfter
)
3179 else if (!dstInEffectBefore
&& dstInEffectAfter
)
3181 else if (dstInEffectBefore
&& !dstInEffectAfter
)
3182 isDST
= dstInEffectNow
;
3184 assert(0, "Bad Logic.");
3187 return adjTime
+ convert
!("minutes", "hnsecs")(tzInfo
.Bias
+ tzInfo
.DaylightBias
);
3190 assert(0, "SysTime's constructor threw.");
3193 return adjTime
+ convert
!("minutes", "hnsecs")(tzInfo
.Bias
+ tzInfo
.StandardBias
);
3197 this(string name
, TIME_ZONE_INFORMATION tzInfo
) @trusted immutable pure
3199 super(name
, to
!string(tzInfo
.StandardName
.ptr
), to
!string(tzInfo
.DaylightName
.ptr
));
3204 TIME_ZONE_INFORMATION _tzInfo
;
3212 $(BLUE This function is Posix-Only.)
3214 Sets the local time zone on Posix systems with the TZ
3215 Database name by setting the TZ environment variable.
3217 Unfortunately, there is no way to do it on Windows using the TZ
3218 Database name, so this function only exists on Posix systems.
3220 void setTZEnvVar(string tzDatabaseName
) @safe nothrow;
3224 $(BLUE This function is Posix-Only.)
3226 Clears the TZ environment variable.
3228 void clearTZEnvVar() @safe nothrow;
3230 else version (Posix
)
3232 void setTZEnvVar(string tzDatabaseName
) @trusted nothrow
3234 import core
.stdc
.time
: tzset
;
3235 import core
.sys
.posix
.stdlib
: setenv
;
3236 import std
.internal
.cstring
: tempCString
;
3237 import std
.path
: asNormalizedPath
, chainPath
;
3240 auto value
= asNormalizedPath(tzDatabaseName
);
3242 auto value
= asNormalizedPath(chainPath(PosixTimeZone
.defaultTZDatabaseDir
, tzDatabaseName
));
3243 setenv("TZ", value
.tempCString(), 1);
3248 void clearTZEnvVar() @trusted nothrow
3250 import core
.stdc
.time
: tzset
;
3251 import core
.sys
.posix
.stdlib
: unsetenv
;
3260 Provides the conversions between the IANA time zone database time zone names
3261 (which POSIX systems use) and the time zone names that Windows uses.
3263 Windows uses a different set of time zone names than the IANA time zone
3264 database does, and how they correspond to one another changes over time
3265 (particularly when Microsoft updates Windows).
3266 $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml)
3267 provides the current conversions (which may or may not match up with what's
3268 on a particular Windows box depending on how up-to-date it is), and
3269 parseTZConversions reads in those conversions from windowsZones.xml so that
3270 a D program can use those conversions.
3272 However, it should be noted that the time zone information on Windows is
3273 frequently less accurate than that in the IANA time zone database, and if
3274 someone really wants accurate time zone information, they should use the
3275 IANA time zone database files with $(LREF PosixTimeZone) on Windows rather
3276 than $(LREF WindowsTimeZone), whereas $(LREF WindowsTimeZone) makes more
3277 sense when trying to match what Windows will think the time is in a specific
3280 Also, the IANA time zone database has a lot more time zones than Windows
3284 windowsZonesXMLText = The text from
3285 $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml)
3288 Exception if there is an error while parsing the given XML.
3290 --------------------
3291 // Parse the conversions from a local file.
3292 auto text = std.file.readText("path/to/windowsZones.xml");
3293 auto conversions = parseTZConversions(text);
3295 // Alternatively, grab the XML file from the web at runtime
3296 // and parse it so that it's guaranteed to be up-to-date, though
3297 // that has the downside that the code needs to worry about the
3298 // site being down or unicode.org changing the URL.
3299 auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml";
3300 auto conversions2 = parseTZConversions(std.net.curl.get(url));
3301 --------------------
3303 struct TZConversions
3306 The key is the Windows time zone name, and the value is a list of
3307 IANA TZ database names which are close (currently only ever one, but
3308 it allows for multiple in case it's ever necessary).
3310 string
[][string
] toWindows
;
3313 The key is the IANA time zone database name, and the value is a list of
3314 Windows time zone names which are close (usually only one, but it could
3317 string
[][string
] fromWindows
;
3321 TZConversions
parseTZConversions(string windowsZonesXMLText
) @safe pure
3323 // This is a bit hacky, since it doesn't properly read XML, but it avoids
3324 // needing to pull in std.xml (which we're theoretically replacing at some
3326 import std
.algorithm
.iteration
: uniq
;
3327 import std
.algorithm
.searching
: find
;
3328 import std
.algorithm
.sorting
: sort
;
3329 import std
.array
: array
, split
;
3330 import std
.string
: lineSplitter
;
3332 string
[][string
] win2Nix
;
3333 string
[][string
] nix2Win
;
3335 immutable f1
= `<mapZone other="`;
3336 immutable f2
= `type="`;
3338 foreach (line
; windowsZonesXMLText
.lineSplitter())
3341 // <mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/>
3343 line
= line
.find(f1
);
3346 line
= line
[f1
.length
.. $];
3347 auto next
= line
.find('"');
3348 enforce(!next
.empty
, "Error parsing. Text does not appear to be from windowsZones.xml");
3349 auto win
= line
[0 .. $ - next
.length
];
3350 line
= next
.find(f2
);
3351 enforce(!line
.empty
, "Error parsing. Text does not appear to be from windowsZones.xml");
3352 line
= line
[f2
.length
.. $];
3353 next
= line
.find('"');
3354 enforce(!next
.empty
, "Error parsing. Text does not appear to be from windowsZones.xml");
3355 auto nixes
= line
[0 .. $ - next
.length
].split();
3357 if (auto n
= win
in win2Nix
)
3360 win2Nix
[win
] = nixes
;
3362 foreach (nix
; nixes
)
3364 if (auto w
= nix
in nix2Win
)
3367 nix2Win
[nix
] = [win
];
3371 foreach (key
, ref value
; nix2Win
)
3372 value
= value
.sort().uniq().array();
3373 foreach (key
, ref value
; win2Nix
)
3374 value
= value
.sort().uniq().array();
3376 return TZConversions(nix2Win
, win2Nix
);
3381 import std
.algorithm
.comparison
: equal
;
3382 import std
.algorithm
.iteration
: uniq
;
3383 import std
.algorithm
.sorting
: isSorted
;
3385 // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
3386 auto sampleFileText
=
3387 `<?xml version="1.0" encoding="UTF-8" ?>
3388 <!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
3390 Copyright © 1991-2013 Unicode, Inc.
3391 CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
3392 For terms of use, see http://www.unicode.org/copyright.html
3396 <version number="$Revision$"/>
3399 <mapTimezones otherVersion="7df0005" typeVersion="2015g">
3401 <!-- (UTC-12:00) International Date Line West -->
3402 <mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/>
3403 <mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/>
3405 <!-- (UTC-11:00) Coordinated Universal Time-11 -->
3406 <mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/>
3407 <mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/>
3408 <mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/>
3409 <mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/>
3410 <mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/>
3412 <!-- (UTC-10:00) Hawaii -->
3413 <mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/>
3414 <mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/>
3415 <mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/>
3416 <mapZone other="Hawaiian Standard Time" territory="UM" type="Pacific/Johnston"/>
3417 <mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/>
3418 <mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/>
3420 <!-- (UTC-09:00) Alaska -->
3421 <mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/>
3422 <mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"/>
3425 </supplementalData>`;
3427 auto tzConversions
= parseTZConversions(sampleFileText
);
3428 assert(tzConversions
.toWindows
.length
== 15);
3429 assert(tzConversions
.toWindows
["America/Anchorage"] == ["Alaskan Standard Time"]);
3430 assert(tzConversions
.toWindows
["America/Juneau"] == ["Alaskan Standard Time"]);
3431 assert(tzConversions
.toWindows
["America/Nome"] == ["Alaskan Standard Time"]);
3432 assert(tzConversions
.toWindows
["America/Sitka"] == ["Alaskan Standard Time"]);
3433 assert(tzConversions
.toWindows
["America/Yakutat"] == ["Alaskan Standard Time"]);
3434 assert(tzConversions
.toWindows
["Etc/GMT+10"] == ["Hawaiian Standard Time"]);
3435 assert(tzConversions
.toWindows
["Etc/GMT+11"] == ["UTC-11"]);
3436 assert(tzConversions
.toWindows
["Etc/GMT+12"] == ["Dateline Standard Time"]);
3437 assert(tzConversions
.toWindows
["Pacific/Honolulu"] == ["Hawaiian Standard Time"]);
3438 assert(tzConversions
.toWindows
["Pacific/Johnston"] == ["Hawaiian Standard Time"]);
3439 assert(tzConversions
.toWindows
["Pacific/Midway"] == ["UTC-11"]);
3440 assert(tzConversions
.toWindows
["Pacific/Niue"] == ["UTC-11"]);
3441 assert(tzConversions
.toWindows
["Pacific/Pago_Pago"] == ["UTC-11"]);
3442 assert(tzConversions
.toWindows
["Pacific/Rarotonga"] == ["Hawaiian Standard Time"]);
3443 assert(tzConversions
.toWindows
["Pacific/Tahiti"] == ["Hawaiian Standard Time"]);
3445 assert(tzConversions
.fromWindows
.length
== 4);
3446 assert(tzConversions
.fromWindows
["Alaskan Standard Time"] ==
3447 ["America/Anchorage", "America/Juneau", "America/Nome", "America/Sitka", "America/Yakutat"]);
3448 assert(tzConversions
.fromWindows
["Dateline Standard Time"] == ["Etc/GMT+12"]);
3449 assert(tzConversions
.fromWindows
["Hawaiian Standard Time"] ==
3450 ["Etc/GMT+10", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Rarotonga", "Pacific/Tahiti"]);
3451 assert(tzConversions
.fromWindows
["UTC-11"] ==
3452 ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"]);
3454 foreach (key
, value
; tzConversions
.fromWindows
)
3456 assert(value
.isSorted
, key
);
3457 assert(equal(value
.uniq(), value
), key
);
3462 // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
3463 deprecated("Use parseTZConversions instead")
3464 string
tzDatabaseNameToWindowsTZName(string tzName
) @safe pure nothrow @nogc
3468 case "Africa/Abidjan": return "Greenwich Standard Time";
3469 case "Africa/Accra": return "Greenwich Standard Time";
3470 case "Africa/Addis_Ababa": return "E. Africa Standard Time";
3471 case "Africa/Algiers": return "W. Central Africa Standard Time";
3472 case "Africa/Asmera": return "E. Africa Standard Time";
3473 case "Africa/Bamako": return "Greenwich Standard Time";
3474 case "Africa/Bangui": return "W. Central Africa Standard Time";
3475 case "Africa/Banjul": return "Greenwich Standard Time";
3476 case "Africa/Bissau": return "Greenwich Standard Time";
3477 case "Africa/Blantyre": return "South Africa Standard Time";
3478 case "Africa/Brazzaville": return "W. Central Africa Standard Time";
3479 case "Africa/Bujumbura": return "South Africa Standard Time";
3480 case "Africa/Cairo": return "Egypt Standard Time";
3481 case "Africa/Casablanca": return "Morocco Standard Time";
3482 case "Africa/Ceuta": return "Romance Standard Time";
3483 case "Africa/Conakry": return "Greenwich Standard Time";
3484 case "Africa/Dakar": return "Greenwich Standard Time";
3485 case "Africa/Dar_es_Salaam": return "E. Africa Standard Time";
3486 case "Africa/Djibouti": return "E. Africa Standard Time";
3487 case "Africa/Douala": return "W. Central Africa Standard Time";
3488 case "Africa/El_Aaiun": return "Morocco Standard Time";
3489 case "Africa/Freetown": return "Greenwich Standard Time";
3490 case "Africa/Gaborone": return "South Africa Standard Time";
3491 case "Africa/Harare": return "South Africa Standard Time";
3492 case "Africa/Johannesburg": return "South Africa Standard Time";
3493 case "Africa/Juba": return "E. Africa Standard Time";
3494 case "Africa/Kampala": return "E. Africa Standard Time";
3495 case "Africa/Khartoum": return "E. Africa Standard Time";
3496 case "Africa/Kigali": return "South Africa Standard Time";
3497 case "Africa/Kinshasa": return "W. Central Africa Standard Time";
3498 case "Africa/Lagos": return "W. Central Africa Standard Time";
3499 case "Africa/Libreville": return "W. Central Africa Standard Time";
3500 case "Africa/Lome": return "Greenwich Standard Time";
3501 case "Africa/Luanda": return "W. Central Africa Standard Time";
3502 case "Africa/Lubumbashi": return "South Africa Standard Time";
3503 case "Africa/Lusaka": return "South Africa Standard Time";
3504 case "Africa/Malabo": return "W. Central Africa Standard Time";
3505 case "Africa/Maputo": return "South Africa Standard Time";
3506 case "Africa/Maseru": return "South Africa Standard Time";
3507 case "Africa/Mbabane": return "South Africa Standard Time";
3508 case "Africa/Mogadishu": return "E. Africa Standard Time";
3509 case "Africa/Monrovia": return "Greenwich Standard Time";
3510 case "Africa/Nairobi": return "E. Africa Standard Time";
3511 case "Africa/Ndjamena": return "W. Central Africa Standard Time";
3512 case "Africa/Niamey": return "W. Central Africa Standard Time";
3513 case "Africa/Nouakchott": return "Greenwich Standard Time";
3514 case "Africa/Ouagadougou": return "Greenwich Standard Time";
3515 case "Africa/Porto-Novo": return "W. Central Africa Standard Time";
3516 case "Africa/Sao_Tome": return "Greenwich Standard Time";
3517 case "Africa/Tripoli": return "Libya Standard Time";
3518 case "Africa/Tunis": return "W. Central Africa Standard Time";
3519 case "Africa/Windhoek": return "Namibia Standard Time";
3520 case "America/Adak": return "Aleutian Standard Time";
3521 case "America/Anchorage": return "Alaskan Standard Time";
3522 case "America/Anguilla": return "SA Western Standard Time";
3523 case "America/Antigua": return "SA Western Standard Time";
3524 case "America/Araguaina": return "SA Eastern Standard Time";
3525 case "America/Argentina/La_Rioja": return "Argentina Standard Time";
3526 case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time";
3527 case "America/Argentina/Salta": return "Argentina Standard Time";
3528 case "America/Argentina/San_Juan": return "Argentina Standard Time";
3529 case "America/Argentina/San_Luis": return "Argentina Standard Time";
3530 case "America/Argentina/Tucuman": return "Argentina Standard Time";
3531 case "America/Argentina/Ushuaia": return "Argentina Standard Time";
3532 case "America/Arguaina": return "Tocantins Standard Time";
3533 case "America/Aruba": return "SA Western Standard Time";
3534 case "America/Asuncion": return "Paraguay Standard Time";
3535 case "America/Bahia": return "Bahia Standard Time";
3536 case "America/Bahia_Banderas": return "Central Standard Time (Mexico)";
3537 case "America/Barbados": return "SA Western Standard Time";
3538 case "America/Belem": return "SA Eastern Standard Time";
3539 case "America/Belize": return "Central America Standard Time";
3540 case "America/Blanc-Sablon": return "SA Western Standard Time";
3541 case "America/Boa_Vista": return "SA Western Standard Time";
3542 case "America/Bogota": return "SA Pacific Standard Time";
3543 case "America/Boise": return "Mountain Standard Time";
3544 case "America/Buenos_Aires": return "Argentina Standard Time";
3545 case "America/Cambridge_Bay": return "Mountain Standard Time";
3546 case "America/Campo_Grande": return "Central Brazilian Standard Time";
3547 case "America/Cancun": return "Eastern Standard Time (Mexico)";
3548 case "America/Caracas": return "Venezuela Standard Time";
3549 case "America/Catamarca": return "Argentina Standard Time";
3550 case "America/Cayenne": return "SA Eastern Standard Time";
3551 case "America/Cayman": return "SA Pacific Standard Time";
3552 case "America/Chicago": return "Central Standard Time";
3553 case "America/Chihuahua": return "Mountain Standard Time (Mexico)";
3554 case "America/Coral_Harbour": return "SA Pacific Standard Time";
3555 case "America/Cordoba": return "Argentina Standard Time";
3556 case "America/Costa_Rica": return "Central America Standard Time";
3557 case "America/Creston": return "US Mountain Standard Time";
3558 case "America/Cuiaba": return "Central Brazilian Standard Time";
3559 case "America/Curacao": return "SA Western Standard Time";
3560 case "America/Danmarkshavn": return "UTC";
3561 case "America/Dawson": return "Pacific Standard Time";
3562 case "America/Dawson_Creek": return "US Mountain Standard Time";
3563 case "America/Denver": return "Mountain Standard Time";
3564 case "America/Detroit": return "Eastern Standard Time";
3565 case "America/Dominica": return "SA Western Standard Time";
3566 case "America/Edmonton": return "Mountain Standard Time";
3567 case "America/Eirunepe": return "SA Pacific Standard Time";
3568 case "America/El_Salvador": return "Central America Standard Time";
3569 case "America/Fortaleza": return "SA Eastern Standard Time";
3570 case "America/Glace_Bay": return "Atlantic Standard Time";
3571 case "America/Godthab": return "Greenland Standard Time";
3572 case "America/Goose_Bay": return "Atlantic Standard Time";
3573 case "America/Grand_Turk": return "Turks And Caicos Standard Time";
3574 case "America/Grenada": return "SA Western Standard Time";
3575 case "America/Guadeloupe": return "SA Western Standard Time";
3576 case "America/Guatemala": return "Central America Standard Time";
3577 case "America/Guayaquil": return "SA Pacific Standard Time";
3578 case "America/Guyana": return "SA Western Standard Time";
3579 case "America/Halifax": return "Atlantic Standard Time";
3580 case "America/Havana": return "Cuba Standard Time";
3581 case "America/Hermosillo": return "US Mountain Standard Time";
3582 case "America/Indiana/Knox": return "Central Standard Time";
3583 case "America/Indiana/Marengo": return "US Eastern Standard Time";
3584 case "America/Indiana/Petersburg": return "Eastern Standard Time";
3585 case "America/Indiana/Tell_City": return "Central Standard Time";
3586 case "America/Indiana/Vevay": return "US Eastern Standard Time";
3587 case "America/Indiana/Vincennes": return "Eastern Standard Time";
3588 case "America/Indiana/Winamac": return "Eastern Standard Time";
3589 case "America/Indianapolis": return "US Eastern Standard Time";
3590 case "America/Inuvik": return "Mountain Standard Time";
3591 case "America/Iqaluit": return "Eastern Standard Time";
3592 case "America/Jamaica": return "SA Pacific Standard Time";
3593 case "America/Jujuy": return "Argentina Standard Time";
3594 case "America/Juneau": return "Alaskan Standard Time";
3595 case "America/Kentucky/Monticello": return "Eastern Standard Time";
3596 case "America/Kralendijk": return "SA Western Standard Time";
3597 case "America/La_Paz": return "SA Western Standard Time";
3598 case "America/Lima": return "SA Pacific Standard Time";
3599 case "America/Los_Angeles": return "Pacific Standard Time";
3600 case "America/Louisville": return "Eastern Standard Time";
3601 case "America/Lower_Princes": return "SA Western Standard Time";
3602 case "America/Maceio": return "SA Eastern Standard Time";
3603 case "America/Managua": return "Central America Standard Time";
3604 case "America/Manaus": return "SA Western Standard Time";
3605 case "America/Marigot": return "SA Western Standard Time";
3606 case "America/Martinique": return "SA Western Standard Time";
3607 case "America/Matamoros": return "Central Standard Time";
3608 case "America/Mazatlan": return "Mountain Standard Time (Mexico)";
3609 case "America/Mendoza": return "Argentina Standard Time";
3610 case "America/Menominee": return "Central Standard Time";
3611 case "America/Merida": return "Central Standard Time (Mexico)";
3612 case "America/Mexico_City": return "Central Standard Time (Mexico)";
3613 case "America/Miquelon": return "Saint Pierre Standard Time";
3614 case "America/Moncton": return "Atlantic Standard Time";
3615 case "America/Monterrey": return "Central Standard Time (Mexico)";
3616 case "America/Montevideo": return "Montevideo Standard Time";
3617 case "America/Montreal": return "Eastern Standard Time";
3618 case "America/Montserrat": return "SA Western Standard Time";
3619 case "America/Nassau": return "Eastern Standard Time";
3620 case "America/New_York": return "Eastern Standard Time";
3621 case "America/Nipigon": return "Eastern Standard Time";
3622 case "America/Nome": return "Alaskan Standard Time";
3623 case "America/Noronha": return "UTC-02";
3624 case "America/North_Dakota/Beulah": return "Central Standard Time";
3625 case "America/North_Dakota/Center": return "Central Standard Time";
3626 case "America/North_Dakota/New_Salem": return "Central Standard Time";
3627 case "America/Ojinaga": return "Mountain Standard Time";
3628 case "America/Panama": return "SA Pacific Standard Time";
3629 case "America/Pangnirtung": return "Eastern Standard Time";
3630 case "America/Paramaribo": return "SA Eastern Standard Time";
3631 case "America/Phoenix": return "US Mountain Standard Time";
3632 case "America/Port-au-Prince": return "Haiti Standard Time";
3633 case "America/Port_of_Spain": return "SA Western Standard Time";
3634 case "America/Porto_Velho": return "SA Western Standard Time";
3635 case "America/Puerto_Rico": return "SA Western Standard Time";
3636 case "America/Rainy_River": return "Central Standard Time";
3637 case "America/Rankin_Inlet": return "Central Standard Time";
3638 case "America/Recife": return "SA Eastern Standard Time";
3639 case "America/Regina": return "Canada Central Standard Time";
3640 case "America/Resolute": return "Central Standard Time";
3641 case "America/Rio_Branco": return "SA Pacific Standard Time";
3642 case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)";
3643 case "America/Santarem": return "SA Eastern Standard Time";
3644 case "America/Santiago": return "Pacific SA Standard Time";
3645 case "America/Santo_Domingo": return "SA Western Standard Time";
3646 case "America/Sao_Paulo": return "E. South America Standard Time";
3647 case "America/Scoresbysund": return "Azores Standard Time";
3648 case "America/Sitka": return "Alaskan Standard Time";
3649 case "America/St_Barthelemy": return "SA Western Standard Time";
3650 case "America/St_Johns": return "Newfoundland Standard Time";
3651 case "America/St_Kitts": return "SA Western Standard Time";
3652 case "America/St_Lucia": return "SA Western Standard Time";
3653 case "America/St_Thomas": return "SA Western Standard Time";
3654 case "America/St_Vincent": return "SA Western Standard Time";
3655 case "America/Swift_Current": return "Canada Central Standard Time";
3656 case "America/Tegucigalpa": return "Central America Standard Time";
3657 case "America/Thule": return "Atlantic Standard Time";
3658 case "America/Thunder_Bay": return "Eastern Standard Time";
3659 case "America/Tijuana": return "Pacific Standard Time";
3660 case "America/Toronto": return "Eastern Standard Time";
3661 case "America/Tortola": return "SA Western Standard Time";
3662 case "America/Vancouver": return "Pacific Standard Time";
3663 case "America/Whitehorse": return "Pacific Standard Time";
3664 case "America/Winnipeg": return "Central Standard Time";
3665 case "America/Yakutat": return "Alaskan Standard Time";
3666 case "America/Yellowknife": return "Mountain Standard Time";
3667 case "Antarctica/Casey": return "W. Australia Standard Time";
3668 case "Antarctica/Davis": return "SE Asia Standard Time";
3669 case "Antarctica/DumontDUrville": return "West Pacific Standard Time";
3670 case "Antarctica/Macquarie": return "Central Pacific Standard Time";
3671 case "Antarctica/Mawson": return "West Asia Standard Time";
3672 case "Antarctica/McMurdo": return "New Zealand Standard Time";
3673 case "Antarctica/Palmer": return "Pacific SA Standard Time";
3674 case "Antarctica/Rothera": return "SA Eastern Standard Time";
3675 case "Antarctica/Syowa": return "E. Africa Standard Time";
3676 case "Antarctica/Vostok": return "Central Asia Standard Time";
3677 case "Arctic/Longyearbyen": return "W. Europe Standard Time";
3678 case "Asia/Aden": return "Arab Standard Time";
3679 case "Asia/Almaty": return "Central Asia Standard Time";
3680 case "Asia/Amman": return "Jordan Standard Time";
3681 case "Asia/Anadyr": return "Russia Time Zone 11";
3682 case "Asia/Aqtau": return "West Asia Standard Time";
3683 case "Asia/Aqtobe": return "West Asia Standard Time";
3684 case "Asia/Ashgabat": return "West Asia Standard Time";
3685 case "Asia/Baghdad": return "Arabic Standard Time";
3686 case "Asia/Bahrain": return "Arab Standard Time";
3687 case "Asia/Baku": return "Azerbaijan Standard Time";
3688 case "Asia/Bangkok": return "SE Asia Standard Time";
3689 case "Asia/Barnaul": return "Altai Standard Time";
3690 case "Asia/Beirut": return "Middle East Standard Time";
3691 case "Asia/Bishkek": return "Central Asia Standard Time";
3692 case "Asia/Brunei": return "Singapore Standard Time";
3693 case "Asia/Calcutta": return "India Standard Time";
3694 case "Asia/Chita": return "Transbaikal Standard Time";
3695 case "Asia/Choibalsan": return "Ulaanbaatar Standard Time";
3696 case "Asia/Colombo": return "Sri Lanka Standard Time";
3697 case "Asia/Damascus": return "Syria Standard Time";
3698 case "Asia/Dhaka": return "Bangladesh Standard Time";
3699 case "Asia/Dili": return "Tokyo Standard Time";
3700 case "Asia/Dubai": return "Arabian Standard Time";
3701 case "Asia/Dushanbe": return "West Asia Standard Time";
3702 case "Asia/Hebron": return "West Bank Standard Time";
3703 case "Asia/Hong_Kong": return "China Standard Time";
3704 case "Asia/Hovd": return "W. Mongolia Standard Time";
3705 case "Asia/Irkutsk": return "North Asia East Standard Time";
3706 case "Asia/Jakarta": return "SE Asia Standard Time";
3707 case "Asia/Jayapura": return "Tokyo Standard Time";
3708 case "Asia/Jerusalem": return "Israel Standard Time";
3709 case "Asia/Kabul": return "Afghanistan Standard Time";
3710 case "Asia/Kamchatka": return "Russia Time Zone 11";
3711 case "Asia/Karachi": return "Pakistan Standard Time";
3712 case "Asia/Katmandu": return "Nepal Standard Time";
3713 case "Asia/Khandyga": return "Yakutsk Standard Time";
3714 case "Asia/Krasnoyarsk": return "North Asia Standard Time";
3715 case "Asia/Kuala_Lumpur": return "Singapore Standard Time";
3716 case "Asia/Kuching": return "Singapore Standard Time";
3717 case "Asia/Kuwait": return "Arab Standard Time";
3718 case "Asia/Macau": return "China Standard Time";
3719 case "Asia/Magadan": return "Magadan Standard Time";
3720 case "Asia/Makassar": return "Singapore Standard Time";
3721 case "Asia/Manila": return "Singapore Standard Time";
3722 case "Asia/Muscat": return "Arabian Standard Time";
3723 case "Asia/Nicosia": return "GTB Standard Time";
3724 case "Asia/Novokuznetsk": return "North Asia Standard Time";
3725 case "Asia/Novosibirsk": return "N. Central Asia Standard Time";
3726 case "Asia/Omsk": return "N. Central Asia Standard Time";
3727 case "Asia/Oral": return "West Asia Standard Time";
3728 case "Asia/Phnom_Penh": return "SE Asia Standard Time";
3729 case "Asia/Pontianak": return "SE Asia Standard Time";
3730 case "Asia/Pyongyang": return "North Korea Standard Time";
3731 case "Asia/Qatar": return "Arab Standard Time";
3732 case "Asia/Qyzylorda": return "Central Asia Standard Time";
3733 case "Asia/Rangoon": return "Myanmar Standard Time";
3734 case "Asia/Riyadh": return "Arab Standard Time";
3735 case "Asia/Saigon": return "SE Asia Standard Time";
3736 case "Asia/Sakhalin": return "Sakhalin Standard Time";
3737 case "Asia/Samarkand": return "West Asia Standard Time";
3738 case "Asia/Seoul": return "Korea Standard Time";
3739 case "Asia/Shanghai": return "China Standard Time";
3740 case "Asia/Singapore": return "Singapore Standard Time";
3741 case "Asia/Srednekolymsk": return "Russia Time Zone 10";
3742 case "Asia/Taipei": return "Taipei Standard Time";
3743 case "Asia/Tashkent": return "West Asia Standard Time";
3744 case "Asia/Tbilisi": return "Georgian Standard Time";
3745 case "Asia/Tehran": return "Iran Standard Time";
3746 case "Asia/Thimphu": return "Bangladesh Standard Time";
3747 case "Asia/Tokyo": return "Tokyo Standard Time";
3748 case "Asia/Tomsk": return "Tomsk Standard Time";
3749 case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time";
3750 case "Asia/Urumqi": return "Central Asia Standard Time";
3751 case "Asia/Ust-Nera": return "Vladivostok Standard Time";
3752 case "Asia/Vientiane": return "SE Asia Standard Time";
3753 case "Asia/Vladivostok": return "Vladivostok Standard Time";
3754 case "Asia/Yakutsk": return "Yakutsk Standard Time";
3755 case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time";
3756 case "Asia/Yerevan": return "Caucasus Standard Time";
3757 case "Atlantic/Azores": return "Azores Standard Time";
3758 case "Atlantic/Bermuda": return "Atlantic Standard Time";
3759 case "Atlantic/Canary": return "GMT Standard Time";
3760 case "Atlantic/Cape_Verde": return "Cape Verde Standard Time";
3761 case "Atlantic/Faeroe": return "GMT Standard Time";
3762 case "Atlantic/Madeira": return "GMT Standard Time";
3763 case "Atlantic/Reykjavik": return "Greenwich Standard Time";
3764 case "Atlantic/South_Georgia": return "UTC-02";
3765 case "Atlantic/St_Helena": return "Greenwich Standard Time";
3766 case "Atlantic/Stanley": return "SA Eastern Standard Time";
3767 case "Australia/Adelaide": return "Cen. Australia Standard Time";
3768 case "Australia/Brisbane": return "E. Australia Standard Time";
3769 case "Australia/Broken_Hill": return "Cen. Australia Standard Time";
3770 case "Australia/Currie": return "Tasmania Standard Time";
3771 case "Australia/Darwin": return "AUS Central Standard Time";
3772 case "Australia/Eucla": return "Aus Central W. Standard Time";
3773 case "Australia/Hobart": return "Tasmania Standard Time";
3774 case "Australia/Lindeman": return "E. Australia Standard Time";
3775 case "Australia/Lord_Howe": return "Lord Howe Standard Time";
3776 case "Australia/Melbourne": return "AUS Eastern Standard Time";
3777 case "Australia/Perth": return "W. Australia Standard Time";
3778 case "Australia/Sydney": return "AUS Eastern Standard Time";
3779 case "CST6CDT": return "Central Standard Time";
3780 case "EST5EDT": return "Eastern Standard Time";
3781 case "Etc/GMT": return "UTC";
3782 case "Etc/GMT+1": return "Cape Verde Standard Time";
3783 case "Etc/GMT+10": return "Hawaiian Standard Time";
3784 case "Etc/GMT+11": return "UTC-11";
3785 case "Etc/GMT+12": return "Dateline Standard Time";
3786 case "Etc/GMT+2": return "UTC-02";
3787 case "Etc/GMT+3": return "SA Eastern Standard Time";
3788 case "Etc/GMT+4": return "SA Western Standard Time";
3789 case "Etc/GMT+5": return "SA Pacific Standard Time";
3790 case "Etc/GMT+6": return "Central America Standard Time";
3791 case "Etc/GMT+7": return "US Mountain Standard Time";
3792 case "Etc/GMT+8": return "UTC-08";
3793 case "Etc/GMT+9": return "UTC-09";
3794 case "Etc/GMT-1": return "W. Central Africa Standard Time";
3795 case "Etc/GMT-10": return "West Pacific Standard Time";
3796 case "Etc/GMT-11": return "Central Pacific Standard Time";
3797 case "Etc/GMT-12": return "UTC+12";
3798 case "Etc/GMT-13": return "Tonga Standard Time";
3799 case "Etc/GMT-14": return "Line Islands Standard Time";
3800 case "Etc/GMT-2": return "South Africa Standard Time";
3801 case "Etc/GMT-3": return "E. Africa Standard Time";
3802 case "Etc/GMT-4": return "Arabian Standard Time";
3803 case "Etc/GMT-5": return "West Asia Standard Time";
3804 case "Etc/GMT-6": return "Central Asia Standard Time";
3805 case "Etc/GMT-7": return "SE Asia Standard Time";
3806 case "Etc/GMT-8": return "Singapore Standard Time";
3807 case "Etc/GMT-9": return "Tokyo Standard Time";
3808 case "Europe/Amsterdam": return "W. Europe Standard Time";
3809 case "Europe/Andorra": return "W. Europe Standard Time";
3810 case "Europe/Astrakhan": return "Astrakhan Standard Time";
3811 case "Europe/Athens": return "GTB Standard Time";
3812 case "Europe/Belgrade": return "Central Europe Standard Time";
3813 case "Europe/Berlin": return "W. Europe Standard Time";
3814 case "Europe/Bratislava": return "Central Europe Standard Time";
3815 case "Europe/Brussels": return "Romance Standard Time";
3816 case "Europe/Bucharest": return "GTB Standard Time";
3817 case "Europe/Budapest": return "Central Europe Standard Time";
3818 case "Europe/Busingen": return "W. Europe Standard Time";
3819 case "Europe/Chisinau": return "GTB Standard Time";
3820 case "Europe/Copenhagen": return "Romance Standard Time";
3821 case "Europe/Dublin": return "GMT Standard Time";
3822 case "Europe/Gibraltar": return "W. Europe Standard Time";
3823 case "Europe/Guernsey": return "GMT Standard Time";
3824 case "Europe/Helsinki": return "FLE Standard Time";
3825 case "Europe/Isle_of_Man": return "GMT Standard Time";
3826 case "Europe/Istanbul": return "Turkey Standard Time";
3827 case "Europe/Jersey": return "GMT Standard Time";
3828 case "Europe/Kaliningrad": return "Kaliningrad Standard Time";
3829 case "Europe/Kiev": return "FLE Standard Time";
3830 case "Europe/Lisbon": return "GMT Standard Time";
3831 case "Europe/Ljubljana": return "Central Europe Standard Time";
3832 case "Europe/London": return "GMT Standard Time";
3833 case "Europe/Luxembourg": return "W. Europe Standard Time";
3834 case "Europe/Madrid": return "Romance Standard Time";
3835 case "Europe/Malta": return "W. Europe Standard Time";
3836 case "Europe/Mariehamn": return "FLE Standard Time";
3837 case "Europe/Minsk": return "Belarus Standard Time";
3838 case "Europe/Monaco": return "W. Europe Standard Time";
3839 case "Europe/Moscow": return "Russian Standard Time";
3840 case "Europe/Oslo": return "W. Europe Standard Time";
3841 case "Europe/Paris": return "Romance Standard Time";
3842 case "Europe/Podgorica": return "Central Europe Standard Time";
3843 case "Europe/Prague": return "Central Europe Standard Time";
3844 case "Europe/Riga": return "FLE Standard Time";
3845 case "Europe/Rome": return "W. Europe Standard Time";
3846 case "Europe/Samara": return "Russia Time Zone 3";
3847 case "Europe/San_Marino": return "W. Europe Standard Time";
3848 case "Europe/Sarajevo": return "Central European Standard Time";
3849 case "Europe/Simferopol": return "Russian Standard Time";
3850 case "Europe/Skopje": return "Central European Standard Time";
3851 case "Europe/Sofia": return "FLE Standard Time";
3852 case "Europe/Stockholm": return "W. Europe Standard Time";
3853 case "Europe/Tallinn": return "FLE Standard Time";
3854 case "Europe/Tirane": return "Central Europe Standard Time";
3855 case "Europe/Uzhgorod": return "FLE Standard Time";
3856 case "Europe/Vaduz": return "W. Europe Standard Time";
3857 case "Europe/Vatican": return "W. Europe Standard Time";
3858 case "Europe/Vienna": return "W. Europe Standard Time";
3859 case "Europe/Vilnius": return "FLE Standard Time";
3860 case "Europe/Volgograd": return "Russian Standard Time";
3861 case "Europe/Warsaw": return "Central European Standard Time";
3862 case "Europe/Zagreb": return "Central European Standard Time";
3863 case "Europe/Zaporozhye": return "FLE Standard Time";
3864 case "Europe/Zurich": return "W. Europe Standard Time";
3865 case "Indian/Antananarivo": return "E. Africa Standard Time";
3866 case "Indian/Chagos": return "Central Asia Standard Time";
3867 case "Indian/Christmas": return "SE Asia Standard Time";
3868 case "Indian/Cocos": return "Myanmar Standard Time";
3869 case "Indian/Comoro": return "E. Africa Standard Time";
3870 case "Indian/Kerguelen": return "West Asia Standard Time";
3871 case "Indian/Mahe": return "Mauritius Standard Time";
3872 case "Indian/Maldives": return "West Asia Standard Time";
3873 case "Indian/Mauritius": return "Mauritius Standard Time";
3874 case "Indian/Mayotte": return "E. Africa Standard Time";
3875 case "Indian/Reunion": return "Mauritius Standard Time";
3876 case "MST7MDT": return "Mountain Standard Time";
3877 case "PST8PDT": return "Pacific Standard Time";
3878 case "Pacific/Apia": return "Samoa Standard Time";
3879 case "Pacific/Auckland": return "New Zealand Standard Time";
3880 case "Pacific/Bougainville": return "Bougainville Standard Time";
3881 case "Pacific/Chatham": return "Chatham Islands Standard Time";
3882 case "Pacific/Easter": return "Easter Island Standard Time";
3883 case "Pacific/Efate": return "Central Pacific Standard Time";
3884 case "Pacific/Enderbury": return "Tonga Standard Time";
3885 case "Pacific/Fakaofo": return "Tonga Standard Time";
3886 case "Pacific/Fiji": return "Fiji Standard Time";
3887 case "Pacific/Funafuti": return "UTC+12";
3888 case "Pacific/Galapagos": return "Central America Standard Time";
3889 case "Pacific/Guadalcanal": return "Central Pacific Standard Time";
3890 case "Pacific/Guam": return "West Pacific Standard Time";
3891 case "Pacific/Honolulu": return "Hawaiian Standard Time";
3892 case "Pacific/Johnston": return "Hawaiian Standard Time";
3893 case "Pacific/Kiritimati": return "Line Islands Standard Time";
3894 case "Pacific/Kosrae": return "Central Pacific Standard Time";
3895 case "Pacific/Kwajalein": return "UTC+12";
3896 case "Pacific/Majuro": return "UTC+12";
3897 case "Pacific/Marquesas": return "Marquesas Standard Time";
3898 case "Pacific/Midway": return "UTC-11";
3899 case "Pacific/Nauru": return "UTC+12";
3900 case "Pacific/Niue": return "UTC-11";
3901 case "Pacific/Noumea": return "Central Pacific Standard Time";
3902 case "Pacific/Norfolk": return "Norfolk Standard Time";
3903 case "Pacific/Pago_Pago": return "UTC-11";
3904 case "Pacific/Palau": return "Tokyo Standard Time";
3905 case "Pacific/Ponape": return "Central Pacific Standard Time";
3906 case "Pacific/Port_Moresby": return "West Pacific Standard Time";
3907 case "Pacific/Rarotonga": return "Hawaiian Standard Time";
3908 case "Pacific/Saipan": return "West Pacific Standard Time";
3909 case "Pacific/Tahiti": return "Hawaiian Standard Time";
3910 case "Pacific/Tarawa": return "UTC+12";
3911 case "Pacific/Tongatapu": return "Tonga Standard Time";
3912 case "Pacific/Truk": return "West Pacific Standard Time";
3913 case "Pacific/Wake": return "UTC+12";
3914 case "Pacific/Wallis": return "UTC+12";
3915 default: return null;
3919 version (Windows
) version (UpdateWindowsTZTranslations
) deprecated @system unittest
3921 import std
.stdio
: stderr
;
3923 foreach (tzName
; TimeZone
.getInstalledTZNames())
3925 if (tzDatabaseNameToWindowsTZName(tzName
) is null)
3926 stderr
.writeln("Missing TZName to Windows translation: ", tzName
);
3931 // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
3932 deprecated("Use parseTZConversions instead")
3933 string
windowsTZNameToTZDatabaseName(string tzName
) @safe pure nothrow @nogc
3937 case "AUS Central Standard Time": return "Australia/Darwin";
3938 case "AUS Eastern Standard Time": return "Australia/Sydney";
3939 case "Aus Central W. Standard Time": return "Australia/Eucla";
3940 case "Afghanistan Standard Time": return "Asia/Kabul";
3941 case "Haiti Standard Time": return "America/Port-au-Prince";
3942 case "Alaskan Standard Time": return "America/Anchorage";
3943 case "Aleutian Standard Time": return "America/Adak";
3944 case "Altai Standard Time": return "Asia/Barnaul";
3945 case "Arab Standard Time": return "Asia/Riyadh";
3946 case "Arabian Standard Time": return "Asia/Dubai";
3947 case "Arabic Standard Time": return "Asia/Baghdad";
3948 case "Argentina Standard Time": return "America/Buenos_Aires";
3949 case "Astrakhan Standard Time": return "Europe/Astrakhan";
3950 case "Atlantic Standard Time": return "America/Halifax";
3951 case "Azerbaijan Standard Time": return "Asia/Baku";
3952 case "Azores Standard Time": return "Atlantic/Azores";
3953 case "Bahia Standard Time": return "America/Bahia";
3954 case "Bangladesh Standard Time": return "Asia/Dhaka";
3955 case "Belarus Standard Time": return "Europe/Minsk";
3956 case "Bougainville Standard Time": return "Pacific/Bougainville";
3957 case "Canada Central Standard Time": return "America/Regina";
3958 case "Cape Verde Standard Time": return "Atlantic/Cape_Verde";
3959 case "Caucasus Standard Time": return "Asia/Yerevan";
3960 case "Cen. Australia Standard Time": return "Australia/Adelaide";
3961 case "Central America Standard Time": return "America/Guatemala";
3962 case "Central Asia Standard Time": return "Asia/Almaty";
3963 case "Central Brazilian Standard Time": return "America/Cuiaba";
3964 case "Central Europe Standard Time": return "Europe/Budapest";
3965 case "Central European Standard Time": return "Europe/Warsaw";
3966 case "Central Pacific Standard Time": return "Pacific/Guadalcanal";
3967 case "Central Standard Time": return "America/Chicago";
3968 case "Central Standard Time (Mexico)": return "America/Mexico_City";
3969 case "Chatham Islands Standard Time": return "Pacific/Chatham";
3970 case "China Standard Time": return "Asia/Shanghai";
3971 case "Cuba Standard Time": return "America/Havana";
3972 case "Dateline Standard Time": return "Etc/GMT+12";
3973 case "E. Africa Standard Time": return "Africa/Nairobi";
3974 case "E. Australia Standard Time": return "Australia/Brisbane";
3975 // This doesn't appear to be in the current stuff from MS, but the autotester
3976 // is failing without it (probably because its time zone data hasn't been
3977 // updated recently enough).
3978 case "E. Europe Standard Time": return "Europe/Minsk";
3979 case "E. South America Standard Time": return "America/Sao_Paulo";
3980 case "Easter Island Standard Time": return "Pacific/Easter";
3981 case "Eastern Standard Time": return "America/New_York";
3982 case "Eastern Standard Time (Mexico)": return "America/Cancun";
3983 case "Egypt Standard Time": return "Africa/Cairo";
3984 case "Ekaterinburg Standard Time": return "Asia/Yekaterinburg";
3985 case "FLE Standard Time": return "Europe/Kiev";
3986 case "Fiji Standard Time": return "Pacific/Fiji";
3987 case "GMT Standard Time": return "Europe/London";
3988 case "GTB Standard Time": return "Europe/Athens";
3989 case "Georgian Standard Time": return "Asia/Tbilisi";
3990 case "Greenland Standard Time": return "America/Godthab";
3991 case "Greenwich Standard Time": return "Atlantic/Reykjavik";
3992 case "Hawaiian Standard Time": return "Pacific/Honolulu";
3993 case "India Standard Time": return "Asia/Calcutta";
3994 case "Iran Standard Time": return "Asia/Tehran";
3995 case "Israel Standard Time": return "Asia/Jerusalem";
3996 case "Jordan Standard Time": return "Asia/Amman";
3997 case "Kaliningrad Standard Time": return "Europe/Kaliningrad";
3998 // Same as with E. Europe Standard Time.
3999 case "Kamchatka Standard Time": return "Asia/Kamchatka";
4000 case "Korea Standard Time": return "Asia/Seoul";
4001 case "Libya Standard Time": return "Africa/Tripoli";
4002 case "Line Islands Standard Time": return "Pacific/Kiritimati";
4003 case "Lord Howe Standard Time": return "Australia/Lord_Howe";
4004 case "Magadan Standard Time": return "Asia/Magadan";
4005 case "Marquesas Standard Time": return "Pacific/Marquesas";
4006 case "Mauritius Standard Time": return "Indian/Mauritius";
4007 // Same as with E. Europe Standard Time.
4008 case "Mexico Standard Time": return "America/Mexico_City";
4009 // Same as with E. Europe Standard Time.
4010 case "Mexico Standard Time 2": return "America/Chihuahua";
4011 // Same as with E. Europe Standard Time.
4012 case "Mid-Atlantic Standard Time": return "Etc/GMT+2";
4013 case "Middle East Standard Time": return "Asia/Beirut";
4014 case "Montevideo Standard Time": return "America/Montevideo";
4015 case "Morocco Standard Time": return "Africa/Casablanca";
4016 case "Mountain Standard Time": return "America/Denver";
4017 case "Mountain Standard Time (Mexico)": return "America/Chihuahua";
4018 case "Myanmar Standard Time": return "Asia/Rangoon";
4019 case "N. Central Asia Standard Time": return "Asia/Novosibirsk";
4020 case "Namibia Standard Time": return "Africa/Windhoek";
4021 case "Nepal Standard Time": return "Asia/Katmandu";
4022 case "New Zealand Standard Time": return "Pacific/Auckland";
4023 case "Newfoundland Standard Time": return "America/St_Johns";
4024 case "Norfolk Standard Time": return "Pacific/Norfolk";
4025 case "North Asia East Standard Time": return "Asia/Irkutsk";
4026 case "North Asia Standard Time": return "Asia/Krasnoyarsk";
4027 case "North Korea Standard Time": return "Asia/Pyongyang";
4028 case "Pacific SA Standard Time": return "America/Santiago";
4029 case "Pacific Standard Time": return "America/Los_Angeles";
4030 case "Pacific Standard Time (Mexico)": return "America/Santa_Isabel";
4031 case "Pakistan Standard Time": return "Asia/Karachi";
4032 case "Paraguay Standard Time": return "America/Asuncion";
4033 case "Romance Standard Time": return "Europe/Paris";
4034 case "Russia Time Zone 10": return "Asia/Srednekolymsk";
4035 case "Russia Time Zone 11": return "Asia/Anadyr";
4036 case "Russia Time Zone 3": return "Europe/Samara";
4037 case "Russian Standard Time": return "Europe/Moscow";
4038 case "SA Eastern Standard Time": return "America/Cayenne";
4039 case "SA Pacific Standard Time": return "America/Bogota";
4040 case "SA Western Standard Time": return "America/La_Paz";
4041 case "SE Asia Standard Time": return "Asia/Bangkok";
4042 case "Sakhalin Standard Time": return "Asia/Sakhalin";
4043 case "Saint Pierre Standard Time": return "America/Miquelon";
4044 case "Samoa Standard Time": return "Pacific/Apia";
4045 case "Singapore Standard Time": return "Asia/Singapore";
4046 case "South Africa Standard Time": return "Africa/Johannesburg";
4047 case "Sri Lanka Standard Time": return "Asia/Colombo";
4048 case "Syria Standard Time": return "Asia/Damascus";
4049 case "Taipei Standard Time": return "Asia/Taipei";
4050 case "Tasmania Standard Time": return "Australia/Hobart";
4051 case "Tocantins Standard Time": return "America/Arguaina";
4052 case "Tokyo Standard Time": return "Asia/Tokyo";
4053 case "Tomsk Standard Time": return "Asia/Tomsk";
4054 case "Tonga Standard Time": return "Pacific/Tongatapu";
4055 case "Transbaikal Standard Time": return "Asia/Chita";
4056 case "Turkey Standard Time": return "Europe/Istanbul";
4057 case "Turks And Caicos Standard Time": return "America/Grand_Turk";
4058 case "US Eastern Standard Time": return "America/Indianapolis";
4059 case "US Mountain Standard Time": return "America/Phoenix";
4060 case "UTC": return "Etc/GMT";
4061 case "UTC+12": return "Etc/GMT-12";
4062 case "UTC-02": return "Etc/GMT+2";
4063 case "UTC-08": return "Etc/GMT+8";
4064 case "UTC-09": return "Etc/GMT+9";
4065 case "UTC-11": return "Etc/GMT+11";
4066 case "Ulaanbaatar Standard Time": return "Asia/Ulaanbaatar";
4067 case "Venezuela Standard Time": return "America/Caracas";
4068 case "Vladivostok Standard Time": return "Asia/Vladivostok";
4069 case "W. Australia Standard Time": return "Australia/Perth";
4070 case "W. Central Africa Standard Time": return "Africa/Lagos";
4071 case "W. Europe Standard Time": return "Europe/Berlin";
4072 case "W. Mongolia Standard Time": return "Asia/Hovd";
4073 case "West Asia Standard Time": return "Asia/Tashkent";
4074 case "West Bank Standard Time": return "Asia/Hebron";
4075 case "West Pacific Standard Time": return "Pacific/Port_Moresby";
4076 case "Yakutsk Standard Time": return "Asia/Yakutsk";
4077 default: return null;
4081 version (Windows
) version (UpdateWindowsTZTranslations
) deprecated @system unittest
4083 import std
.stdio
: stderr
;
4085 foreach (winName
; WindowsTimeZone
.getInstalledTZNames())
4087 if (windowsTZNameToTZDatabaseName(winName
) is null)
4088 stderr
.writeln("Missing Windows to TZName translation: ", winName
);
4093 // This script is for regenerating tzDatabaseNameToWindowsTZName and
4094 // windowsTZNameToTZDatabaseName from
4095 // http://unicode.org/cldr/data/common/supplemental/windowsZones.xml
4100 import std.algorithm;
4103 import std.datetime;
4104 import std.exception;
4109 int main(string[] args)
4111 if (args.length != 4 || args[1].baseName != "windowsZones.xml")
4113 stderr.writeln("genTZs.d windowsZones.xml <nix2WinFile> <win2NixFile>");
4117 string[][string] win2Nix;
4118 string[][string] nix2Win;
4119 immutable f1 = `<mapZone other="`;
4120 immutable f2 = `type="`;
4122 auto file = File(args[1]);
4123 foreach (line; file.byLine())
4125 line = line.find(f1);
4128 line = line[f1.length .. $];
4129 auto next = line.find('"');
4130 auto win = to!string(line[0 .. $ - next.length]);
4131 line = next.find(f2);
4132 line = line[f2.length .. $];
4133 next = line.find('"');
4134 auto nixes = to!string(line[0 .. $ - next.length]).split();
4136 if (auto l = win in win2Nix)
4139 win2Nix[win] = nixes;
4140 foreach (nix; nixes)
4142 if (auto w = nix in nix2Win)
4145 nix2Win[nix] = [win];
4149 foreach (nix; nix2Win.byKey())
4151 auto wins = nix2Win[nix];
4152 nix2Win[nix] = wins.sort().uniq().array();
4155 foreach (win; win2Nix.byKey())
4157 auto nixes = win2Nix[win];
4158 win2Nix[win] = nixes.sort().uniq().array();
4161 // AFAIK, there should be no cases of a TZ Database time zone converting to
4162 // multiple windows time zones.
4163 foreach (nix, wins; nix2Win)
4164 enforce(wins.length == 1, format("%s -> %s", nix, wins));
4166 // We'll try to eliminate multiples by favoring a conversion if it's already
4167 // in Phobos, but if it's new, then the correct one will have to be chosen
4168 // manually from the results.
4169 string[] haveMultiple;
4170 foreach (win, nixes; win2Nix)
4172 if (nixes.length > 1)
4173 haveMultiple ~= win;
4175 bool[string] haveConflicts;
4176 foreach (win; haveMultiple)
4178 if (auto curr = windowsTZNameToTZDatabaseName(win))
4180 if (auto other = curr in nix2Win)
4182 if ((*other)[0] == win)
4184 win2Nix[win] = [curr];
4189 haveConflicts[win] = true;
4190 writefln("Warning: %s -> %s", win, win2Nix[win]);
4194 string[] nix2WinLines = [
4195 `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`,
4200 foreach (nix; nix2Win.keys.sort())
4201 nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]);
4204 ` default: return null;`,
4209 string[] win2NixLines = [
4210 `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`,
4214 foreach (win; win2Nix.keys.sort())
4216 immutable hasMultiple = cast(bool)(win in haveConflicts);
4217 foreach (nix; win2Nix[win])
4218 win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : "");
4222 ` default: return null;`,
4227 auto nix2WinFile = args[2];
4228 std.file.write(nix2WinFile, nix2WinLines.join("\n"));
4230 auto win2NixFile = args[3];
4231 std.file.write(win2NixFile, win2NixLines.join("\n"));