Wolfgang Thallergcc16 merge934c3885 months agoHistory
3550 lines · 126.7 KB · D
RawDownload
// Written in the D programming language /++ $(SCRIPT inhibitQuickIndex = 1;)$(DIVC quickindex,$(BOOKTABLE,$(TR $(TH Category) $(TH Functions))$(TR $(TD Time zones) $(TD    $(LREF TimeZone)    $(LREF UTC)    $(LREF LocalTime)    $(LREF PosixTimeZone)    $(LREF WindowsTimeZone)    $(LREF SimpleTimeZone)))$(TR $(TD Utilities) $(TD    $(LREF clearTZEnvVar)    $(LREF parseTZConversions)    $(LREF setTZEnvVar)    $(LREF TZConversions)))))     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).    Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis)    Source:    $(PHOBOSSRC std/datetime/timezone.d)+/module std.datetime.timezone; import core.time : abs, convert, dur, Duration, hours, minutes;import std.datetime.systime : Clock, stdTimeToUnixTime, SysTime;import std.range.primitives : back, empty, front, isOutputRange, popFront;import std.traits : isIntegral, isSomeString; version (OSX)    version = Darwin;else version (iOS)    version = Darwin;else version (TVOS)    version = Darwin;else version (WatchOS)    version = Darwin; version (Windows){    import core.stdc.time : time_t;    import core.sys.windows.winbase;    import core.sys.windows.winsock2;    import std.windows.registry;     // Uncomment and run unittests to print missing Windows TZ translations.    // Please subscribe to Microsoft Daylight Saving Time & Time Zone Blog    // (https://blogs.technet.microsoft.com/dst2007/) if you feel responsible    // for updating the translations.    // version = UpdateWindowsTZTranslations;}else version (Posix){    import core.sys.posix.signal : timespec;    import core.sys.posix.sys.types : time_t;} version (StdUnittest) import std.exception : assertThrown;  /++    Represents a time zone. It is used with $(REF SysTime,std,datetime,systime)    to indicate the time zone of a $(REF SysTime,std,datetime,systime).  +/abstract class TimeZone{public:     /++        The name of the time zone. Exactly how the time zone name is formatted        depends on the derived class. In the case of $(LREF PosixTimeZone), it's        the TZ Database name, whereas with $(LREF WindowsTimeZone), it's the        name that Windows chose to give the registry key for that time zone        (typically the name that they give $(LREF stdTime) if the OS is in        English). For other time zone types, what it is depends on how they're        implemented.         See_Also:            $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ              Database)<br>            $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of              Time Zones)      +/    @property string name() @safe const nothrow    {        return _name;    }      /++        Typically, the abbreviation (generally 3 or 4 letters) for the time zone        when DST is $(I not) in effect (e.g. PST). It is not necessarily unique.         However, on Windows, it may be the unabbreviated name (e.g. Pacific        Standard Time). Regardless, it is not the same as name.      +/    @property string stdName() @safe const scope nothrow    {        return _stdName;    }      /++        Typically, the abbreviation (generally 3 or 4 letters) for the time zone        when DST $(I is) in effect (e.g. PDT). It is not necessarily unique.         However, on Windows, it may be the unabbreviated name (e.g. Pacific        Daylight Time). Regardless, it is not the same as name.      +/    @property string dstName() @safe const scope nothrow    {        return _dstName;    }      /++        Whether this time zone has Daylight Savings Time at any point in time.        Note that for some time zone types it may not have DST for current dates        but will still return true for `hasDST` because the time zone did at        some point have DST.      +/    @property abstract bool hasDST() @safe const nothrow;      /++        Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.        in UTC time (i.e. std time) and returns whether DST is effect in this        time zone at the given point in time.         Params:            stdTime = The UTC time that needs to be checked for DST in this time                      zone.      +/    abstract bool dstInEffect(long stdTime) @safe const scope nothrow;      /++        Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.        in UTC time (i.e. std time) and converts it to this time zone's time.         Params:            stdTime = The UTC time that needs to be adjusted to this time zone's                      time.      +/    abstract long utcToTZ(long stdTime) @safe const scope nothrow;      /++        Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D.        in this time zone's time and converts it to UTC (i.e. std time).         Params:            adjTime = The time in this time zone that needs to be adjusted to                      UTC time.      +/    abstract long tzToUTC(long adjTime) @safe const scope nothrow;      /++        Returns what the offset from UTC is at the given std time.        It includes the DST offset in effect at that time (if any).         Params:            stdTime = The UTC time for which to get the offset from UTC for this                      time zone.      +/    Duration utcOffsetAt(long stdTime) @safe const scope nothrow    {        return dur!"hnsecs"(utcToTZ(stdTime) - stdTime);    }     // The purpose of this is to handle the case where a Windows time zone is    // new and exists on an up-to-date Windows box but does not exist on Windows    // boxes which have not been properly updated. The "date added" is included    // on the theory that we'll be able to remove them at some point in the    // the future once enough time has passed, and that way, we know how much    // time has passed.    private static string _getOldName(string windowsTZName) @safe pure nothrow    {        switch (windowsTZName)        {            case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08            case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08            case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08            case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08            default: return null;        }    }     // Since reading in the time zone files could be expensive, most unit tests    // are consolidated into this one unittest block which minimizes how often    // it reads a time zone file.    @system unittest    {        import core.exception : AssertError;        import std.conv : to;        import std.file : exists, isFile;        import std.format : format;        import std.path : chainPath;        import std.stdio : writefln;        import std.typecons : tuple;         version (Posix) alias getTimeZone = PosixTimeZone.getTimeZone;        else version (Windows) alias getTimeZone = WindowsTimeZone.getTimeZone;         version (Posix) scope(exit) clearTZEnvVar();         static immutable(TimeZone) testTZ(string tzName,                                          string stdName,                                          string dstName,                                          Duration utcOffset,                                          Duration dstOffset,                                          bool north = true)        {            scope(failure) writefln("Failed time zone: %s", tzName);             version (Posix)            {                immutable tz = PosixTimeZone.getTimeZone(tzName);                assert(tz.name == tzName);            }            else version (Windows)            {                immutable tz = WindowsTimeZone.getTimeZone(tzName);                assert(tz.name == stdName);            }             immutable hasDST = dstOffset != Duration.zero;             //assert(tz.stdName == stdName);  //Locale-dependent            //assert(tz.dstName == dstName);  //Locale-dependent            assert(tz.hasDST == hasDST);             import std.datetime.date : DateTime;            immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0);            immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0);            auto std = SysTime(stdDate, tz);            auto dst = SysTime(dstDate, tz);            auto stdUTC = SysTime(stdDate - utcOffset, UTC());            auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC());             assert(!std.dstInEffect);            assert(dst.dstInEffect == hasDST);            assert(tz.utcOffsetAt(std.stdTime) == utcOffset);            assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset);             assert(cast(DateTime) std == stdDate);            assert(cast(DateTime) dst == dstDate);            assert(std == stdUTC);             version (Posix)            {                setTZEnvVar(tzName);                 static void testTM(scope const SysTime st)                {                    import core.stdc.time : tm;                    import core.sys.posix.time : localtime_r;                     time_t unixTime = st.toUnixTime();                    tm osTimeInfo = void;                    localtime_r(&unixTime, &osTimeInfo);                    tm ourTimeInfo = st.toTM();                     assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec);                    assert(ourTimeInfo.tm_min == osTimeInfo.tm_min);                    assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour);                    assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday);                    assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon);                    assert(ourTimeInfo.tm_year == osTimeInfo.tm_year);                    assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday);                    assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday);                    assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst);                    assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff);                    assert(to!string(ourTimeInfo.tm_zone) == to!string(osTimeInfo.tm_zone));                }                 testTM(std);                testTM(dst);                 // Apparently, right/ does not exist on Mac OS X. I don't know                // whether or not it exists on FreeBSD. It's rather pointless                // normally, since the Posix standard requires that leap seconds                // be ignored, so it does make some sense that right/ wouldn't                // be there, but since PosixTimeZone _does_ use leap seconds if                // the time zone file does, we'll test that functionality if the                // appropriate files exist.                if (chainPath(PosixTimeZone.getDefaultTZDatabaseDir(), "right", tzName).exists)                {                    auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName);                     assert(leapTZ.name == "right/" ~ tzName);                    //assert(leapTZ.stdName == stdName);  //Locale-dependent                    //assert(leapTZ.dstName == dstName);  //Locale-dependent                    assert(leapTZ.hasDST == hasDST);                     auto leapSTD = SysTime(std.stdTime, leapTZ);                    auto leapDST = SysTime(dst.stdTime, leapTZ);                     assert(!leapSTD.dstInEffect);                    assert(leapDST.dstInEffect == hasDST);                     assert(leapSTD.stdTime == std.stdTime);                    assert(leapDST.stdTime == dst.stdTime);                     // Whenever a leap second is added/removed,                    // this will have to be adjusted.                    //enum leapDiff = convert!("seconds", "hnsecs")(25);                    //assert(leapSTD.adjTime - leapDiff == std.adjTime);                    //assert(leapDST.adjTime - leapDiff == dst.adjTime);                }            }             return tz;        }         import std.datetime.date : DateTime;        auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11),  DateTime(2012, 11, 4), 2, 2),                            /+America/New_York+/    tuple(DateTime(2012, 3, 11),  DateTime(2012, 11, 4), 2, 2),                            ///+America/Santiago+/    tuple(DateTime(2011, 8, 21),  DateTime(2011, 5, 8), 0, 0),                            /+Europe/London+/       tuple(DateTime(2012, 3, 25),  DateTime(2012, 10, 28), 1, 2),                            /+Europe/Paris+/        tuple(DateTime(2012, 3, 25),  DateTime(2012, 10, 28), 2, 3),                            /+Australia/Adelaide+/  tuple(DateTime(2012, 10, 7),  DateTime(2012, 4, 1), 2, 3)];         import std.datetime.date : DateTimeException;        version (Posix)        {            version (FreeBSD)            enum utcZone = "Etc/UTC";            else version (OpenBSD)       enum utcZone = "UTC";            else version (NetBSD)        enum utcZone = "UTC";            else version (DragonFlyBSD)  enum utcZone = "UTC";            else version (linux)         enum utcZone = "UTC";            else version (Darwin)        enum utcZone = "UTC";            else version (Solaris)       enum utcZone = "UTC";            else static assert(0, "The location of the UTC timezone file on this Posix platform must be set.");             auto tzs = [testTZ("America/Los_Angeles", "PST", "PDT", dur!"hours"(-8), dur!"hours"(1)),                        testTZ("America/New_York", "EST", "EDT", dur!"hours"(-5), dur!"hours"(1)),                        //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false),                        testTZ("Europe/London", "GMT", "BST", dur!"hours"(0), dur!"hours"(1)),                        testTZ("Europe/Paris", "CET", "CEST", dur!"hours"(1), dur!"hours"(1)),                        // Per www.timeanddate.com, it should be "CST" and "CDT",                        // but the OS insists that it's "CST" for both. We should                        // probably figure out how to report an error in the TZ                        // database and report it.                        testTZ("Australia/Adelaide", "CST", "CST",                               dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)];             testTZ(utcZone, "UTC", "UTC", dur!"hours"(0), dur!"hours"(0));            assertThrown!DateTimeException(PosixTimeZone.getTimeZone("hello_world"));        }        else version (Windows)        {            auto tzs = [testTZ("Pacific Standard Time", "Pacific Standard Time",                               "Pacific Daylight Time", dur!"hours"(-8), dur!"hours"(1)),                        testTZ("Eastern Standard Time", "Eastern Standard Time",                               "Eastern Daylight Time", dur!"hours"(-5), dur!"hours"(1)),                        //testTZ("Pacific SA Standard Time", "Pacific SA Standard Time",                               //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false),                        testTZ("GMT Standard Time", "GMT Standard Time",                               "GMT Daylight Time", dur!"hours"(0), dur!"hours"(1)),                        testTZ("Romance Standard Time", "Romance Standard Time",                               "Romance Daylight Time", dur!"hours"(1), dur!"hours"(1)),                        testTZ("Cen. Australia Standard Time", "Cen. Australia Standard Time",                               "Cen. Australia Daylight Time",                               dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)];             testTZ("Greenwich Standard Time", "Greenwich Standard Time",                   "Greenwich Daylight Time", dur!"hours"(0), dur!"hours"(0));            assertThrown!DateTimeException(WindowsTimeZone.getTimeZone("hello_world"));        }        else            assert(0, "OS not supported.");         foreach (i; 0 .. tzs.length)        {            auto tz = tzs[i];            immutable spring = dstSwitches[i][2];            immutable fall = dstSwitches[i][3];            auto stdOffset = SysTime(dstSwitches[i][0] + dur!"days"(-1), tz).utcOffset;            auto dstOffset = stdOffset + dur!"hours"(1);             // Verify that creating a SysTime in the given time zone results            // in a SysTime with the correct std time during and surrounding            // a DST switch.            foreach (hour; -12 .. 13)            {                import std.exception : enforce;                auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz);                immutable targetHour = hour < 0 ? hour + 24 : hour;                 static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__)                {                    enforce(st.hour == hour,                            new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour),                                            __FILE__, line));                }                 void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__)                {                    AssertError msg(string tag)                    {                        return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]",                                                      tag, st, tz.name, st.utcOffset, stdOffset, dstOffset),                                               __FILE__, line);                    }                     enforce(st.dstInEffect == dstInEffect, msg("1"));                    enforce(st.utcOffset == offset, msg("2"));                    enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3"));                }                 if (hour == spring)                {                    testHour(st, spring + 1, tz.name);                    testHour(st + dur!"minutes"(1), spring + 1, tz.name);                }                else                {                    testHour(st, targetHour, tz.name);                    testHour(st + dur!"minutes"(1), targetHour, tz.name);                }                 if (hour < spring)                    testOffset1(stdOffset, false);                else                    testOffset1(dstOffset, true);                 st = SysTime(dstSwitches[i][1] + dur!"hours"(hour), tz);                testHour(st, targetHour, tz.name);                 // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is).                if (hour == fall - 1)                    testHour(st + dur!"hours"(1), targetHour, tz.name);                 if (hour < fall)                    testOffset1(dstOffset, true);                else                    testOffset1(stdOffset, false);            }             // Verify that converting a time in UTC to a time in another            // time zone results in the correct time during and surrounding            // a DST switch.            bool first = true;            auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset;            auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset;            // https://issues.dlang.org/show_bug.cgi?id=3659 makes this necessary.            auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1);             foreach (hour; -24 .. 25)            {                auto utc = SysTime(dstSwitches[i][0] + dur!"hours"(hour), UTC());                auto local = utc.toOtherTZ(tz);                 void testOffset2(Duration offset, size_t line = __LINE__)                {                    AssertError msg(string tag)                    {                        return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tz.name, utc, local),                                               __FILE__, line);                    }                     import std.exception : enforce;                    enforce((utc + offset).hour == local.hour, msg("1"));                    enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2"));                }                 if (utc < springSwitch)                    testOffset2(stdOffset);                else                    testOffset2(dstOffset);                 utc = SysTime(dstSwitches[i][1] + dur!"hours"(hour), UTC());                local = utc.toOtherTZ(tz);                 if (utc == fallSwitch || utc == fallSwitchMinus1)                {                    if (first)                    {                        testOffset2(dstOffset);                        first = false;                    }                    else                        testOffset2(stdOffset);                }                else if (utc > fallSwitch)                    testOffset2(stdOffset);                else                    testOffset2(dstOffset);            }        }    } 

Lines 1–500 of 3550.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t