blob: e80f1225155121dec57e06cf891aa02aede2a31d [file] [log] [blame]
// Written in the D programming language
/++
$(SCRIPT inhibitQuickIndex = 1;)
$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Functions))
$(TR $(TD Types) $(TD
$(LREF Clock)
$(LREF SysTime)
$(LREF DosFileTime)
))
$(TR $(TD Conversion) $(TD
$(LREF parseRFC822DateTime)
$(LREF DosFileTimeToSysTime)
$(LREF FILETIMEToStdTime)
$(LREF FILETIMEToSysTime)
$(LREF stdTimeToFILETIME)
$(LREF stdTimeToUnixTime)
$(LREF SYSTEMTIMEToSysTime)
$(LREF SysTimeToDosFileTime)
$(LREF SysTimeToFILETIME)
$(LREF SysTimeToSYSTEMTIME)
$(LREF unixTimeToStdTime)
))
))
License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
Source: $(PHOBOSSRC std/datetime/systime.d)
+/
module std.datetime.systime;
version (OSX)
version = Darwin;
else version (iOS)
version = Darwin;
else version (TVOS)
version = Darwin;
else version (WatchOS)
version = Darwin;
/// Get the current time as a $(LREF SysTime)
@safe unittest
{
import std.datetime.timezone : LocalTime;
SysTime today = Clock.currTime();
assert(today.timezone is LocalTime());
}
/// Construct a $(LREF SysTime) from a ISO time string
@safe unittest
{
import std.datetime.date : DateTime;
import std.datetime.timezone : UTC;
auto st = SysTime.fromISOExtString("2018-01-01T10:30:00Z");
assert(st == SysTime(DateTime(2018, 1, 1, 10, 30, 0), UTC()));
}
/// Make a specific point in time in the New York timezone
@safe unittest
{
import core.time : hours;
import std.datetime.date : DateTime;
import std.datetime.timezone : SimpleTimeZone;
auto ny = SysTime(
DateTime(2018, 1, 1, 10, 30, 0),
new immutable SimpleTimeZone(-5.hours, "America/New_York")
);
// ISO standard time strings
assert(ny.toISOString() == "20180101T103000-05:00");
assert(ny.toISOExtString() == "2018-01-01T10:30:00-05:00");
}
// Note: reconsider using specific imports below after
// https://issues.dlang.org/show_bug.cgi?id=17630 has been fixed
import core.time;// : ClockType, convert, dur, Duration, seconds, TimeException;
import std.datetime.date;// : _monthNames, AllowDayOverflow, CmpTimeUnits, Date,
//DateTime, DateTimeException, DayOfWeek, enforceValid, getDayOfWeek, maxDay,
//Month, splitUnitsFromHNSecs, TimeOfDay, validTimeUnits, yearIsLeapYear;
import std.datetime.timezone;// : LocalTime, SimpleTimeZone, TimeZone, UTC;
import std.exception : enforce;
import std.format : format;
import std.range.primitives;
import std.traits : isIntegral, isSigned, isSomeString, isNarrowString;
version (Windows)
{
import core.stdc.time : time_t;
import core.sys.windows.winbase;
import core.sys.windows.winnt;
import core.sys.windows.winsock2;
}
else version (Posix)
{
import core.sys.posix.signal : timespec;
import core.sys.posix.sys.types : time_t;
}
version (StdUnittest)
{
import core.exception : AssertError;
import std.exception : assertThrown;
}
@safe unittest
{
initializeTests();
}
version (unittest) private bool clockSupported(ClockType c)
{
// Skip unsupported clocks on older linux kernels, assume that only
// CLOCK_MONOTONIC and CLOCK_REALTIME exist, as that is the lowest
// common denominator supported by all versions of Linux pre-2.6.12.
version (Linux_Pre_2639)
return c == ClockType.normal || c == ClockType.precise;
else
return true;
}
/++
Effectively a namespace to make it clear that the methods it contains are
getting the time from the system clock. It cannot be instantiated.
+/
final class Clock
{
public:
/++
Returns the current time in the given time zone.
Params:
clockType = The $(REF ClockType, core,time) indicates which system
clock to use to get the current time. Very few programs
need to use anything other than the default.
tz = The time zone for the SysTime that's returned.
Throws:
$(REF DateTimeException,std,datetime,date) if it fails to get the
time.
+/
static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe
{
return SysTime(currStdTime!clockType, tz);
}
@safe unittest
{
import std.format : format;
import core.time;
assert(currTime().timezone is LocalTime());
assert(currTime(UTC()).timezone is UTC());
// core.stdc.time.time does not always use unix time on Windows systems.
// In particular, dmc does not use unix time. If we can guarantee that
// the MS runtime uses unix time, then we may be able run this test
// then, but for now, we're just not going to run this test on Windows.
version (Posix)
{
static import core.stdc.time;
static import std.math;
immutable unixTimeD = currTime().toUnixTime();
immutable unixTimeC = core.stdc.time.time(null);
assert(std.math.abs(unixTimeC - unixTimeD) <= 2);
}
auto norm1 = Clock.currTime;
auto norm2 = Clock.currTime(UTC());
assert(norm1 <= norm2, format("%s %s", norm1, norm2));
assert(abs(norm1 - norm2) <= seconds(2));
import std.meta : AliasSeq;
static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second))
{{
static if (clockSupported(ct))
{
auto value1 = Clock.currTime!ct;
auto value2 = Clock.currTime!ct(UTC());
assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct));
assert(abs(value1 - value2) <= seconds(2), format("ClockType.%s", ct));
}
}}
}
/++
Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the
current time.
Params:
clockType = The $(REF ClockType, core,time) indicates which system
clock to use to get the current time. Very few programs
need to use anything other than the default.
Throws:
$(REF DateTimeException,std,datetime,date) if it fails to get the
time.
+/
static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted
{
static if (clockType != ClockType.coarse &&
clockType != ClockType.normal &&
clockType != ClockType.precise &&
clockType != ClockType.second)
{
static assert(0, format("ClockType.%s is not supported by Clock.currTime or Clock.currStdTime", clockType));
}
version (Windows)
{
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
immutable result = FILETIMEToStdTime(&fileTime);
static if (clockType == ClockType.second)
{
// Ideally, this would use core.std.time.time, but the C runtime
// has to be using unix time for that to work, and that's not
// guaranteed on Windows. Digital Mars does not use unix time.
// MS may or may not. If it does, then this can be made to use
// core.stdc.time for MS, but for now, we'll leave it like this.
return convert!("seconds", "hnsecs")(convert!("hnsecs", "seconds")(result));
}
else
return result;
}
else version (Posix)
{
static import core.stdc.time;
enum hnsecsToUnixEpoch = unixTimeToStdTime(0);
version (Darwin)
{
static if (clockType == ClockType.second)
return unixTimeToStdTime(core.stdc.time.time(null));
else
{
import core.sys.posix.sys.time : gettimeofday, timeval;
timeval tv = void;
// Posix gettimeofday called with a valid timeval address
// and a null second parameter doesn't fail.
gettimeofday(&tv, null);
return convert!("seconds", "hnsecs")(tv.tv_sec) +
tv.tv_usec * 10 +
hnsecsToUnixEpoch;
}
}
else version (linux)
{
static if (clockType == ClockType.second)
return unixTimeToStdTime(core.stdc.time.time(null));
else
{
import core.sys.linux.time : CLOCK_REALTIME_COARSE;
import core.sys.posix.time : clock_gettime, CLOCK_REALTIME;
static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE;
else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME;
else static assert(0, "Previous static if is wrong.");
timespec ts = void;
immutable error = clock_gettime(clockArg, &ts);
// Posix clock_gettime called with a valid address and valid clock_id is only
// permitted to fail if the number of seconds does not fit in time_t. If tv_sec
// is long or larger overflow won't happen before 292 billion years A.D.
static if (ts.tv_sec.max < long.max)
{
if (error)
throw new TimeException("Call to clock_gettime() failed");
}
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
}
else version (FreeBSD)
{
import core.sys.freebsd.time : clock_gettime, CLOCK_REALTIME,
CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND;
static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST;
else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE;
else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND;
else static assert(0, "Previous static if is wrong.");
timespec ts = void;
immutable error = clock_gettime(clockArg, &ts);
// Posix clock_gettime called with a valid address and valid clock_id is only
// permitted to fail if the number of seconds does not fit in time_t. If tv_sec
// is long or larger overflow won't happen before 292 billion years A.D.
static if (ts.tv_sec.max < long.max)
{
if (error)
throw new TimeException("Call to clock_gettime() failed");
}
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
else version (NetBSD)
{
static if (clockType == ClockType.second)
return unixTimeToStdTime(core.stdc.time.time(null));
else
{
import core.sys.netbsd.time : clock_gettime, CLOCK_REALTIME;
timespec ts = void;
immutable error = clock_gettime(CLOCK_REALTIME, &ts);
// Posix clock_gettime called with a valid address and valid clock_id is only
// permitted to fail if the number of seconds does not fit in time_t. If tv_sec
// is long or larger overflow won't happen before 292 billion years A.D.
static if (ts.tv_sec.max < long.max)
{
if (error)
throw new TimeException("Call to clock_gettime() failed");
}
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
}
else version (OpenBSD)
{
static if (clockType == ClockType.second)
return unixTimeToStdTime(core.stdc.time.time(null));
else
{
import core.sys.openbsd.time : clock_gettime, CLOCK_REALTIME;
static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME;
else static assert(0, "Previous static if is wrong.");
timespec ts;
if (clock_gettime(clockArg, &ts) != 0)
throw new TimeException("Call to clock_gettime() failed");
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
}
else version (DragonFlyBSD)
{
import core.sys.dragonflybsd.time : clock_gettime, CLOCK_REALTIME,
CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND;
static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST;
else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE;
else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND;
else static assert(0, "Previous static if is wrong.");
timespec ts = void;
immutable error = clock_gettime(clockArg, &ts);
// Posix clock_gettime called with a valid address and valid clock_id is only
// permitted to fail if the number of seconds does not fit in time_t. If tv_sec
// is long or larger overflow won't happen before 292 billion years A.D.
static if (ts.tv_sec.max < long.max)
{
if (error)
throw new TimeException("Call to clock_gettime() failed");
}
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
else version (Solaris)
{
static if (clockType == ClockType.second)
return unixTimeToStdTime(core.stdc.time.time(null));
else
{
import core.sys.solaris.time : clock_gettime, CLOCK_REALTIME;
static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME;
else static assert(0, "Previous static if is wrong.");
timespec ts = void;
immutable error = clock_gettime(clockArg, &ts);
// Posix clock_gettime called with a valid address and valid clock_id is only
// permitted to fail if the number of seconds does not fit in time_t. If tv_sec
// is long or larger overflow won't happen before 292 billion years A.D.
static if (ts.tv_sec.max < long.max)
{
if (error)
throw new TimeException("Call to clock_gettime() failed");
}
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
}
else version (Hurd)
{
static if (clockType == ClockType.second)
return unixTimeToStdTime(core.stdc.time.time(null));
else
{
import core.sys.hurd.time : CLOCK_REALTIME_COARSE;
import core.sys.posix.time : clock_gettime, CLOCK_REALTIME;
static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE;
else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME;
else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME;
else static assert(0, "Previous static if is wrong.");
timespec ts = void;
immutable error = clock_gettime(clockArg, &ts);
// Posix clock_gettime called with a valid address and valid clock_id is only
// permitted to fail if the number of seconds does not fit in time_t. If tv_sec
// is long or larger overflow won't happen before 292 billion years A.D.
static if (ts.tv_sec.max < long.max)
{
if (error)
throw new TimeException("Call to clock_gettime() failed");
}
return convert!("seconds", "hnsecs")(ts.tv_sec) +
ts.tv_nsec / 100 +
hnsecsToUnixEpoch;
}
}
else static assert(0, "Unsupported OS");
}
else static assert(0, "Unsupported OS");
}
@safe unittest
{
import std.format : format;
import std.math.algebraic : abs;
import std.meta : AliasSeq;
enum limit = convert!("seconds", "hnsecs")(2);
auto norm1 = Clock.currStdTime;
auto norm2 = Clock.currStdTime;
assert(norm1 <= norm2, format("%s %s", norm1, norm2));
assert(abs(norm1 - norm2) <= limit);
static foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second))
{{
static if (clockSupported(ct))
{
auto value1 = Clock.currStdTime!ct;
auto value2 = Clock.currStdTime!ct;
assert(value1 <= value2, format("%s %s (ClockType: %s)", value1, value2, ct));
assert(abs(value1 - value2) <= limit);
}
}}
}
private:
@disable this();
}
/// Get the current time as a $(LREF SysTime)
@safe unittest
{
import std.datetime.timezone : LocalTime;
SysTime today = Clock.currTime();
assert(today.timezone is LocalTime());
}
/++
`SysTime` is the type used to get the current time from the
system or doing anything that involves time zones. Unlike
$(REF DateTime,std,datetime,date), the time zone is an integral part of
`SysTime` (though for local time applications, time zones can be ignored
and it will work, since it defaults to using the local time zone). It holds
its internal time in std time (hnsecs since midnight, January 1st, 1 A.D.
UTC), so it interfaces well with the system time. However, that means that,
unlike $(REF DateTime,std,datetime,date), it is not optimized for
calendar-based operations, and getting individual units from it such as
years or days is going to involve conversions and be less efficient.
An $(I hnsec) (hecto-nanosecond) is 100 nanoseconds. There are 10,000,000 hnsecs in a second.
For calendar-based operations that don't
care about time zones, then $(REF DateTime,std,datetime,date) would be
the type to use. For system time, use `SysTime`.
$(LREF Clock.currTime) will return the current time as a `SysTime`.
To convert a `SysTime` to a $(REF Date,std,datetime,date) or
$(REF DateTime,std,datetime,date), simply cast it. To convert a
$(REF Date,std,datetime,date) or $(REF DateTime,std,datetime,date) to a
`SysTime`, use `SysTime`'s constructor, and pass in the intended time
zone with it (or don't pass in a $(REF TimeZone,std,datetime,timezone), and
the local time zone will be used). Be aware, however, that converting from a
$(REF DateTime,std,datetime,date) to a `SysTime` will not necessarily
be 100% accurate due to DST (one hour of the year doesn't exist and another
occurs twice). To not risk any conversion errors, keep times as
`SysTime`s. Aside from DST though, there shouldn't be any conversion
problems.
For using time zones other than local time or UTC, use
$(REF PosixTimeZone,std,datetime,timezone) on Posix systems (or on Windows,
if providing the TZ Database files), and use
$(REF WindowsTimeZone,std,datetime,timezone) on Windows systems. The time in
`SysTime` is kept internally in hnsecs from midnight, January 1st, 1 A.D.
UTC. Conversion error cannot happen when changing the time zone of a
`SysTime`. $(REF LocalTime,std,datetime,timezone) is the
$(REF TimeZone,std,datetime,timezone) class which represents the local time,
and `UTC` is the $(REF TimeZone,std,datetime,timezone) class which
represents UTC. `SysTime` uses $(REF LocalTime,std,datetime,timezone) if
no $(REF TimeZone,std,datetime,timezone) is provided. For more details on
time zones, see the documentation for $(REF TimeZone,std,datetime,timezone),
$(REF PosixTimeZone,std,datetime,timezone), and
$(REF WindowsTimeZone,std,datetime,timezone).
`SysTime`'s range is from approximately 29,000 B.C. to approximately
29,000 A.D.
+/
struct SysTime
{
import core.stdc.time : tm;
version (Posix) import core.sys.posix.sys.time : timeval;
import std.typecons : Rebindable;
public:
/++
Params:
dateTime = The $(REF DateTime,std,datetime,date) to use to set
this $(LREF SysTime)'s internal std time. As
$(REF DateTime,std,datetime,date) has no concept of
time zone, tz is used as its time zone.
tz = The $(REF TimeZone,std,datetime,timezone) to use for this
$(LREF SysTime). If null,
$(REF LocalTime,std,datetime,timezone) will be used. The
given $(REF DateTime,std,datetime,date) is assumed to
be in the given time zone.
+/
this(DateTime dateTime, return scope immutable TimeZone tz = null) return scope @safe nothrow
{
try
this(dateTime, Duration.zero, tz);
catch (Exception e)
assert(0, "SysTime's constructor threw when it shouldn't have.");
}
@safe unittest
{
static void test(DateTime dt, immutable TimeZone tz, long expected)
{
auto sysTime = SysTime(dt, tz);
assert(sysTime._stdTime == expected);
assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given DateTime: %s", dt));
}
test(DateTime.init, UTC(), 0);
test(DateTime(1, 1, 1, 12, 30, 33), UTC(), 450_330_000_000L);
test(DateTime(0, 12, 31, 12, 30, 33), UTC(), -413_670_000_000L);
test(DateTime(1, 1, 1, 0, 0, 0), UTC(), 0);
test(DateTime(1, 1, 1, 0, 0, 1), UTC(), 10_000_000L);
test(DateTime(0, 12, 31, 23, 59, 59), UTC(), -10_000_000L);
test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L);
test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0);
test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L);
static void testScope(scope ref DateTime dt) @safe
{
auto st = SysTime(dt);
}
}
/++
Params:
dateTime = The $(REF DateTime,std,datetime,date) to use to set
this $(LREF SysTime)'s internal std time. As
$(REF DateTime,std,datetime,date) has no concept of
time zone, tz is used as its time zone.
fracSecs = The fractional seconds portion of the time.
tz = The $(REF TimeZone,std,datetime,timezone) to use for this
$(LREF SysTime). If null,
$(REF LocalTime,std,datetime,timezone) will be used. The
given $(REF DateTime,std,datetime,date) is assumed to
be in the given time zone.
Throws:
$(REF DateTimeException,std,datetime,date) if `fracSecs` is negative or if it's
greater than or equal to one second.
+/
this(DateTime dateTime, Duration fracSecs, return scope immutable TimeZone tz = null) return scope @safe
{
enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds."));
enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second."));
auto nonNullTZ = tz is null ? LocalTime() : tz;
immutable dateDiff = dateTime.date - Date.init;
immutable todDiff = dateTime.timeOfDay - TimeOfDay.init;
immutable adjustedTime = dateDiff + todDiff + fracSecs;
immutable standardTime = nonNullTZ.tzToUTC(adjustedTime.total!"hnsecs");
this(standardTime, nonNullTZ);
}
@safe unittest
{
import core.time;
static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected)
{
auto sysTime = SysTime(dt, fracSecs, tz);
assert(sysTime._stdTime == expected);
assert(sysTime._timezone is (tz is null ? LocalTime() : tz),
format("Given DateTime: %s, Given Duration: %s", dt, fracSecs));
}
test(DateTime.init, Duration.zero, UTC(), 0);
test(DateTime(1, 1, 1, 12, 30, 33), Duration.zero, UTC(), 450_330_000_000L);
test(DateTime(0, 12, 31, 12, 30, 33), Duration.zero, UTC(), -413_670_000_000L);
test(DateTime(1, 1, 1, 0, 0, 0), msecs(1), UTC(), 10_000L);
test(DateTime(0, 12, 31, 23, 59, 59), msecs(999), UTC(), -10_000L);
test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC(), -1);
test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC(), -9_999_999);
test(DateTime(0, 12, 31, 23, 59, 59), Duration.zero, UTC(), -10_000_000);
assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC()));
assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC()));
static void testScope(scope ref DateTime dt, scope ref Duration d) @safe
{
auto st = SysTime(dt, d);
}
}
/++
Params:
date = The $(REF Date,std,datetime,date) to use to set this
$(LREF SysTime)'s internal std time. As
$(REF Date,std,datetime,date) has no concept of time zone, tz
is used as its time zone.
tz = The $(REF TimeZone,std,datetime,timezone) to use for this
$(LREF SysTime). If null,
$(REF LocalTime,std,datetime,timezone) will be used. The
given $(REF Date,std,datetime,date) is assumed to be in the
given time zone.
+/
this(Date date, return scope immutable TimeZone tz = null) return scope @safe nothrow
{
_timezone = tz is null ? LocalTime() : tz;
try
{
immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs";
immutable standardTime = _timezone.tzToUTC(adjustedTime);
this(standardTime, _timezone);
}
catch (Exception e)
assert(0, "Date's constructor through when it shouldn't have.");
}
@safe unittest
{
static void test(Date d, immutable TimeZone tz, long expected)
{
auto sysTime = SysTime(d, tz);
assert(sysTime._stdTime == expected);
assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given Date: %s", d));
}
test(Date.init, UTC(), 0);
test(Date(1, 1, 1), UTC(), 0);
test(Date(1, 1, 2), UTC(), 864000000000);
test(Date(0, 12, 31), UTC(), -864000000000);
static void testScope(scope ref Date d) @safe
{
auto st = SysTime(d);
}
}
/++
Note:
Whereas the other constructors take in the given date/time, assume
that it's in the given time zone, and convert it to hnsecs in UTC
since midnight, January 1st, 1 A.D. UTC - i.e. std time - this
constructor takes a std time, which is specifically already in UTC,
so no conversion takes place. Of course, the various getter
properties and functions will use the given time zone's conversion
function to convert the results to that time zone, but no conversion
of the arguments to this constructor takes place.
Params:
stdTime = The number of hnsecs since midnight, January 1st, 1 A.D.
UTC.
tz = The $(REF TimeZone,std,datetime,timezone) to use for this
$(LREF SysTime). If null,
$(REF LocalTime,std,datetime,timezone) will be used.
+/
this(long stdTime, return scope immutable TimeZone tz = null) return scope @safe pure nothrow
{
_stdTime = stdTime;
_timezone = tz is null ? LocalTime() : tz;
}
@safe unittest
{
static void test(long stdTime, immutable TimeZone tz)
{
auto sysTime = SysTime(stdTime, tz);
assert(sysTime._stdTime == stdTime);
assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given stdTime: %s", stdTime));
}
foreach (stdTime; [-1234567890L, -250, 0, 250, 1235657390L])
{
foreach (tz; testTZs)
test(stdTime, tz);
}
}
/++
Params:
rhs = The $(LREF SysTime) to assign to this one.
Returns: The `this` of this `SysTime`.
+/
ref SysTime opAssign()(auto ref const(SysTime) rhs) scope return @safe pure nothrow
{
_stdTime = rhs._stdTime;
_timezone = rhs._timezone;
return this;
}
@safe unittest
{
SysTime st;
st = SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC());
assert(st == SysTime(DateTime(2012, 12, 21, 1, 2, 3), UTC()));
const other = SysTime(DateTime(19, 1, 7, 13, 14, 15), LocalTime());
st = other;
assert(st == other);
version (none) // https://issues.dlang.org/show_bug.cgi?id=21175
static void testScope(scope ref SysTime left, const scope SysTime right) @safe
{
left = right;
}
}
/++
Checks for equality between this $(LREF SysTime) and the given
$(LREF SysTime).
Note that the time zone is ignored. Only the internal
std times (which are in UTC) are compared.
+/
bool opEquals()(auto ref const(SysTime) rhs) @safe const pure nothrow scope
{
return _stdTime == rhs._stdTime;
}
@safe unittest
{
import std.range : chain;
assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC()));
assert(SysTime(DateTime.init, UTC()) == SysTime(0));
assert(SysTime(Date.init, UTC()) == SysTime(0));
assert(SysTime(0) == SysTime(0));
static void test(DateTime dt, immutable TimeZone tz1, immutable TimeZone tz2)
{
auto st1 = SysTime(dt);
st1.timezone = tz1;
auto st2 = SysTime(dt);
st2.timezone = tz2;
assert(st1 == st2);
}
foreach (tz1; testTZs)
{
foreach (tz2; testTZs)
{
foreach (dt; chain(testDateTimesBC, testDateTimesAD))
test(dt, tz1, tz2);
}
}
auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
assert(st == st);
assert(st == cst);
assert(st == ist);
assert(cst == st);
assert(cst == cst);
assert(cst == ist);
assert(ist == st);
assert(ist == cst);
assert(ist == ist);
static void testScope(scope ref SysTime left, const scope SysTime right) @safe
{
assert(left == right);
assert(right == left);
}
}
/++
Compares this $(LREF SysTime) with the given $(LREF SysTime).
Time zone is irrelevant when comparing $(LREF SysTime)s.
Returns:
$(BOOKTABLE,
$(TR $(TD this &lt; rhs) $(TD &lt; 0))
$(TR $(TD this == rhs) $(TD 0))
$(TR $(TD this &gt; rhs) $(TD &gt; 0))
)
+/
int opCmp()(auto ref const(SysTime) rhs) @safe const pure nothrow scope
{
if (_stdTime < rhs._stdTime)
return -1;
if (_stdTime > rhs._stdTime)
return 1;
return 0;
}
@safe unittest
{
import std.algorithm.iteration : map;
import std.array : array;
import std.range : chain;
assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0);
assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0);
assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0);
assert(SysTime(0).opCmp(SysTime(0)) == 0);
static void testEqual(SysTime st, immutable TimeZone tz1, immutable TimeZone tz2)
{
auto st1 = st;
st1.timezone = tz1;
auto st2 = st;
st2.timezone = tz2;
assert(st1.opCmp(st2) == 0);
}
auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD)));
foreach (st; sts)
{
foreach (tz1; testTZs)
{
foreach (tz2; testTZs)
testEqual(st, tz1, tz2);
}
}
static void testCmp(SysTime st1, immutable TimeZone tz1, SysTime st2, immutable TimeZone tz2)
{
st1.timezone = tz1;
st2.timezone = tz2;
assert(st1.opCmp(st2) < 0);
assert(st2.opCmp(st1) > 0);
}
foreach (si, st1; sts)
{
foreach (st2; sts[si + 1 .. $])
{
foreach (tz1; testTZs)
{
foreach (tz2; testTZs)
testCmp(st1, tz1, st2, tz2);
}
}
}
auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30));
assert(st.opCmp(st) == 0);
assert(st.opCmp(cst) == 0);
assert(st.opCmp(ist) == 0);
assert(cst.opCmp(st) == 0);
assert(cst.opCmp(cst) == 0);
assert(cst.opCmp(ist) == 0);
assert(ist.opCmp(st) == 0);
assert(ist.opCmp(cst) == 0);
assert(ist.opCmp(ist) == 0);
static void testScope(scope ref SysTime left, const scope SysTime right) @safe
{
assert(left < right);
assert(right > left);
}
}
/++
Returns: A hash of the $(LREF SysTime).
+/
size_t toHash() const @nogc pure nothrow @safe scope
{
static if (is(size_t == ulong))
return _stdTime;
else
{
// MurmurHash2
enum ulong m = 0xc6a4a7935bd1e995UL;
enum ulong n = m * 16;
enum uint r = 47;
ulong k = _stdTime;
k *= m;
k ^= k >> r;
k *= m;
ulong h = n;
h ^= k;
h *= m;
return cast(size_t) h;
}
}
@safe unittest
{
assert(SysTime(0).toHash == SysTime(0).toHash);
assert(SysTime(DateTime(2000, 1, 1)).toHash == SysTime(DateTime(2000, 1, 1)).toHash);
assert(SysTime(DateTime(2000, 1, 1)).toHash != SysTime(DateTime(2000, 1, 2)).toHash);
// test that timezones aren't taken into account
assert(SysTime(0, LocalTime()).toHash == SysTime(0, LocalTime()).toHash);
assert(SysTime(0, LocalTime()).toHash == SysTime(0, UTC()).toHash);
assert(SysTime(DateTime(2000, 1, 1), LocalTime()).toHash == SysTime(DateTime(2000, 1, 1), LocalTime()).toHash);
immutable zone = new SimpleTimeZone(dur!"minutes"(60));
assert(SysTime(DateTime(2000, 1, 1, 1), zone).toHash == SysTime(DateTime(2000, 1, 1), UTC()).toHash);
assert(SysTime(DateTime(2000, 1, 1), zone).toHash != SysTime(DateTime(2000, 1, 1), UTC()).toHash);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toHash();
}
}
/++
Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
are B.C.
+/
@property short year() @safe const nothrow scope
{
return (cast(Date) this).year;
}
@safe unittest
{
import std.range : chain;
static void test(SysTime sysTime, long expected)
{
assert(sysTime.year == expected, format("Value given: %s", sysTime));
}
test(SysTime(0, UTC()), 1);
test(SysTime(1, UTC()), 1);
test(SysTime(-1, UTC()), 0);
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (tod; testTODs)
{
auto dt = DateTime(Date(year, md.month, md.day), tod);
foreach (tz; testTZs)
{
foreach (fs; testFracSecs)
test(SysTime(dt, fs, tz), year);
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.year == 1999);
assert(ist.year == 1999);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.year;
}
}
/++
Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
are B.C.
Params:
year = The year to set this $(LREF SysTime)'s year to.
Throws:
$(REF DateTimeException,std,datetime,date) if the new year is not
a leap year and the resulting date would be on February 29th.
+/
@property void year(int year) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
auto date = Date(cast(int) days);
date.year = year;
immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1);
adjTime = newDaysHNSecs + hnsecs;
}
///
@safe unittest
{
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999);
assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010);
assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7);
}
@safe unittest
{
import std.range : chain;
static void test(SysTime st, int year, SysTime expected)
{
st.year = year;
assert(st == expected);
}
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
foreach (year; chain(testYearsBC, testYearsAD))
{
auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second),
st.fracSecs,
st.timezone);
test(st, year, e);
}
}
foreach (fs; testFracSecs)
{
foreach (tz; testTZs)
{
foreach (tod; testTODs)
{
test(SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz), 2000,
SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz));
test(SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz), 1999,
SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz));
}
foreach (tod; testTODsThrown)
{
auto st = SysTime(DateTime(Date(2000, 2, 29), tod), fs, tz);
assertThrown!DateTimeException(st.year = 1999);
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.year = 7));
static assert(!__traits(compiles, ist.year = 7));
static void testScope(scope ref SysTime st) @safe
{
st.year = 42;
}
}
/++
Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
Throws:
$(REF DateTimeException,std,datetime,date) if `isAD` is true.
+/
@property ushort yearBC() @safe const scope
{
return (cast(Date) this).yearBC;
}
///
@safe unittest
{
import std.datetime.date : DateTime;
assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1);
assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2);
assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101);
}
@safe unittest
{
import std.exception : assertNotThrown;
foreach (st; testSysTimesBC)
{
auto msg = format("SysTime: %s", st);
assertNotThrown!DateTimeException(st.yearBC, msg);
assert(st.yearBC == (st.year * -1) + 1, msg);
}
foreach (st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]])
assertThrown!DateTimeException(st.yearBC, format("SysTime: %s", st));
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.year = 12;
assert(st.year == 12);
static assert(!__traits(compiles, cst.year = 12));
static assert(!__traits(compiles, ist.year = 12));
static void testScope(scope ref SysTime st) @safe
{
auto result = st.yearBC;
}
}
/++
Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
Params:
year = The year B.C. to set this $(LREF SysTime)'s year to.
Throws:
$(REF DateTimeException,std,datetime,date) if a non-positive value
is given.
+/
@property void yearBC(int year) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
auto date = Date(cast(int) days);
date.yearBC = year;
immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1);
adjTime = newDaysHNSecs + hnsecs;
}
@safe unittest
{
auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0));
st.yearBC = 1;
assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0)));
st.yearBC = 10;
assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0)));
}
@safe unittest
{
import std.range : chain;
static void test(SysTime st, int year, SysTime expected)
{
st.yearBC = year;
assert(st == expected, format("SysTime: %s", st));
}
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
foreach (year; testYearsBC)
{
auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second),
st.fracSecs,
st.timezone);
test(st, (year * -1) + 1, e);
}
}
foreach (st; [testSysTimesBC[0], testSysTimesBC[$ - 1], testSysTimesAD[0], testSysTimesAD[$ - 1]])
{
foreach (year; testYearsBC)
assertThrown!DateTimeException(st.yearBC = year);
}
foreach (fs; testFracSecs)
{
foreach (tz; testTZs)
{
foreach (tod; testTODs)
{
test(SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz), 2001,
SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz));
test(SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz), 2000,
SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz));
}
foreach (tod; testTODsThrown)
{
auto st = SysTime(DateTime(Date(-2000, 2, 29), tod), fs, tz);
assertThrown!DateTimeException(st.year = -1999);
}
}
}
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.yearBC = 12;
assert(st.yearBC == 12);
static assert(!__traits(compiles, cst.yearBC = 12));
static assert(!__traits(compiles, ist.yearBC = 12));
static void testScope(scope ref SysTime st) @safe
{
st.yearBC = 42;
}
}
/++
Month of a Gregorian Year.
+/
@property Month month() @safe const nothrow scope
{
return (cast(Date) this).month;
}
///
@safe unittest
{
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7);
assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10);
assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4);
}
@safe unittest
{
import std.range : chain;
static void test(SysTime sysTime, Month expected)
{
assert(sysTime.month == expected, format("Value given: %s", sysTime));
}
test(SysTime(0, UTC()), Month.jan);
test(SysTime(1, UTC()), Month.jan);
test(SysTime(-1, UTC()), Month.dec);
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (tod; testTODs)
{
auto dt = DateTime(Date(year, md.month, md.day), tod);
foreach (fs; testFracSecs)
{
foreach (tz; testTZs)
test(SysTime(dt, fs, tz), md.month);
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.month == 7);
assert(ist.month == 7);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.month;
}
}
/++
Month of a Gregorian Year.
Params:
month = The month to set this $(LREF SysTime)'s month to.
Throws:
$(REF DateTimeException,std,datetime,date) if the given month is
not a valid month.
+/
@property void month(Month month) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
auto date = Date(cast(int) days);
date.month = month;
immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1);
adjTime = newDaysHNSecs + hnsecs;
}
@safe unittest
{
import std.algorithm.iteration : filter;
import std.range : chain;
static void test(SysTime st, Month month, SysTime expected)
{
st.month = cast(Month) month;
assert(st == expected);
}
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
foreach (md; testMonthDays)
{
if (st.day > maxDay(dt.year, md.month))
continue;
auto e = SysTime(DateTime(dt.year, md.month, dt.day, dt.hour, dt.minute, dt.second),
st.fracSecs,
st.timezone);
test(st, md.month, e);
}
}
foreach (fs; testFracSecs)
{
foreach (tz; testTZs)
{
foreach (tod; testTODs)
{
foreach (year; filter!((a){return yearIsLeapYear(a);}) (chain(testYearsBC, testYearsAD)))
{
test(SysTime(DateTime(Date(year, 1, 29), tod), fs, tz),
Month.feb,
SysTime(DateTime(Date(year, 2, 29), tod), fs, tz));
}
foreach (year; chain(testYearsBC, testYearsAD))
{
test(SysTime(DateTime(Date(year, 1, 28), tod), fs, tz),
Month.feb,
SysTime(DateTime(Date(year, 2, 28), tod), fs, tz));
test(SysTime(DateTime(Date(year, 7, 30), tod), fs, tz),
Month.jun,
SysTime(DateTime(Date(year, 6, 30), tod), fs, tz));
}
}
}
}
foreach (fs; [testFracSecs[0], testFracSecs[$-1]])
{
foreach (tz; testTZs)
{
foreach (tod; testTODsThrown)
{
foreach (year; [testYearsBC[$-3], testYearsBC[$-2],
testYearsBC[$-2], testYearsAD[0],
testYearsAD[$-2], testYearsAD[$-1]])
{
auto day = yearIsLeapYear(year) ? 30 : 29;
auto st1 = SysTime(DateTime(Date(year, 1, day), tod), fs, tz);
assertThrown!DateTimeException(st1.month = Month.feb);
auto st2 = SysTime(DateTime(Date(year, 7, 31), tod), fs, tz);
assertThrown!DateTimeException(st2.month = Month.jun);
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.month = Month.dec));
static assert(!__traits(compiles, ist.month = Month.dec));
static void testScope(scope ref SysTime st) @safe
{
st.month = Month.dec;
}
}
/++
Day of a Gregorian Month.
+/
@property ubyte day() @safe const nothrow scope
{
return (cast(Date) this).day;
}
///
@safe unittest
{
import std.datetime.date : DateTime;
assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6);
assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4);
assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5);
}
@safe unittest
{
import std.range : chain;
static void test(SysTime sysTime, int expected)
{
assert(sysTime.day == expected, format("Value given: %s", sysTime));
}
test(SysTime(0, UTC()), 1);
test(SysTime(1, UTC()), 1);
test(SysTime(-1, UTC()), 31);
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (tod; testTODs)
{
auto dt = DateTime(Date(year, md.month, md.day), tod);
foreach (tz; testTZs)
{
foreach (fs; testFracSecs)
test(SysTime(dt, fs, tz), md.day);
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.day == 6);
assert(ist.day == 6);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.day;
}
}
/++
Day of a Gregorian Month.
Params:
day = The day of the month to set this $(LREF SysTime)'s day to.
Throws:
$(REF DateTimeException,std,datetime,date) if the given day is not
a valid day of the current month.
+/
@property void day(int day) @safe scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
auto date = Date(cast(int) days);
date.day = day;
immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1);
adjTime = newDaysHNSecs + hnsecs;
}
@safe unittest
{
import std.range : chain;
import std.traits : EnumMembers;
foreach (day; chain(testDays))
{
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
if (day > maxDay(dt.year, dt.month))
continue;
auto expected = SysTime(DateTime(dt.year, dt.month, day, dt.hour, dt.minute, dt.second),
st.fracSecs,
st.timezone);
st.day = day;
assert(st == expected, format("[%s] [%s]", st, expected));
}
}
foreach (tz; testTZs)
{
foreach (tod; testTODs)
{
foreach (fs; testFracSecs)
{
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (month; EnumMembers!Month)
{
auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz);
immutable max = maxDay(year, month);
auto expected = SysTime(DateTime(Date(year, month, max), tod), fs, tz);
st.day = max;
assert(st == expected, format("[%s] [%s]", st, expected));
}
}
}
}
}
foreach (tz; testTZs)
{
foreach (tod; testTODsThrown)
{
foreach (fs; [testFracSecs[0], testFracSecs[$-1]])
{
foreach (year; [testYearsBC[$-3], testYearsBC[$-2],
testYearsBC[$-2], testYearsAD[0],
testYearsAD[$-2], testYearsAD[$-1]])
{
foreach (month; EnumMembers!Month)
{
auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz);
immutable max = maxDay(year, month);
assertThrown!DateTimeException(st.day = max + 1);
}
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.day = 27));
static assert(!__traits(compiles, ist.day = 27));
static void testScope(scope ref SysTime st) @safe
{
st.day = 12;
}
}
/++
Hours past midnight.
+/
@property ubyte hour() @safe const nothrow scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
return cast(ubyte) getUnitsFromHNSecs!"hours"(hnsecs);
}
@safe unittest
{
import std.range : chain;
static void test(SysTime sysTime, int expected)
{
assert(sysTime.hour == expected, format("Value given: %s", sysTime));
}
test(SysTime(0, UTC()), 0);
test(SysTime(1, UTC()), 0);
test(SysTime(-1, UTC()), 23);
foreach (tz; testTZs)
{
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (hour; testHours)
{
foreach (minute; testMinSecs)
{
foreach (second; testMinSecs)
{
auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second));
foreach (fs; testFracSecs)
test(SysTime(dt, fs, tz), hour);
}
}
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.hour == 12);
assert(ist.hour == 12);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.hour;
}
}
/++
Hours past midnight.
Params:
hour = The hours to set this $(LREF SysTime)'s hour to.
Throws:
$(REF DateTimeException,std,datetime,date) if the given hour are
not a valid hour of the day.
+/
@property void hour(int hour) @safe scope
{
enforceValid!"hours"(hour);
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs);
immutable daysHNSecs = convert!("days", "hnsecs")(days);
immutable negative = hnsecs < 0;
if (negative)
hnsecs += convert!("hours", "hnsecs")(24);
hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs);
hnsecs += convert!("hours", "hnsecs")(hour);
if (negative)
hnsecs -= convert!("hours", "hnsecs")(24);
adjTime = daysHNSecs + hnsecs;
}
@safe unittest
{
import std.range : chain;
foreach (hour; chain(testHours))
{
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, hour, dt.minute, dt.second),
st.fracSecs,
st.timezone);
st.hour = hour;
assert(st == expected, format("[%s] [%s]", st, expected));
}
}
auto st = testSysTimesAD[0];
assertThrown!DateTimeException(st.hour = -1);
assertThrown!DateTimeException(st.hour = 60);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.hour = 27));
static assert(!__traits(compiles, ist.hour = 27));
static void testScope(scope ref SysTime st) @safe
{
st.hour = 12;
}
}
/++
Minutes past the current hour.
+/
@property ubyte minute() @safe const nothrow scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs);
return cast(ubyte) getUnitsFromHNSecs!"minutes"(hnsecs);
}
@safe unittest
{
import std.range : chain;
static void test(SysTime sysTime, int expected)
{
assert(sysTime.minute == expected, format("Value given: %s", sysTime));
}
test(SysTime(0, UTC()), 0);
test(SysTime(1, UTC()), 0);
test(SysTime(-1, UTC()), 59);
foreach (tz; testTZs)
{
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (hour; testHours)
{
foreach (minute; testMinSecs)
{
foreach (second; testMinSecs)
{
auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second));
foreach (fs; testFracSecs)
test(SysTime(dt, fs, tz), minute);
}
}
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.minute == 30);
assert(ist.minute == 30);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.minute;
}
}
/++
Minutes past the current hour.
Params:
minute = The minute to set this $(LREF SysTime)'s minute to.
Throws:
$(REF DateTimeException,std,datetime,date) if the given minute are
not a valid minute of an hour.
+/
@property void minute(int minute) @safe scope
{
enforceValid!"minutes"(minute);
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs);
immutable daysHNSecs = convert!("days", "hnsecs")(days);
immutable negative = hnsecs < 0;
if (negative)
hnsecs += convert!("hours", "hnsecs")(24);
immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs);
hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs);
hnsecs += convert!("hours", "hnsecs")(hour);
hnsecs += convert!("minutes", "hnsecs")(minute);
if (negative)
hnsecs -= convert!("hours", "hnsecs")(24);
adjTime = daysHNSecs + hnsecs;
}
@safe unittest
{
import std.range : chain;
foreach (minute; testMinSecs)
{
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, minute, dt.second),
st.fracSecs,
st.timezone);
st.minute = minute;
assert(st == expected, format("[%s] [%s]", st, expected));
}
}
auto st = testSysTimesAD[0];
assertThrown!DateTimeException(st.minute = -1);
assertThrown!DateTimeException(st.minute = 60);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.minute = 27));
static assert(!__traits(compiles, ist.minute = 27));
static void testScope(scope ref SysTime st) @safe
{
st.minute = 12;
}
}
/++
Seconds past the current minute.
+/
@property ubyte second() @safe const nothrow scope
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs);
hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs);
return cast(ubyte) getUnitsFromHNSecs!"seconds"(hnsecs);
}
@safe unittest
{
import std.range : chain;
static void test(SysTime sysTime, int expected)
{
assert(sysTime.second == expected, format("Value given: %s", sysTime));
}
test(SysTime(0, UTC()), 0);
test(SysTime(1, UTC()), 0);
test(SysTime(-1, UTC()), 59);
foreach (tz; testTZs)
{
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (hour; testHours)
{
foreach (minute; testMinSecs)
{
foreach (second; testMinSecs)
{
auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second));
foreach (fs; testFracSecs)
test(SysTime(dt, fs, tz), second);
}
}
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.second == 33);
assert(ist.second == 33);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.second;
}
}
/++
Seconds past the current minute.
Params:
second = The second to set this $(LREF SysTime)'s second to.
Throws:
$(REF DateTimeException,std,datetime,date) if the given second are
not a valid second of a minute.
+/
@property void second(int second) @safe scope
{
enforceValid!"seconds"(second);
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs);
immutable daysHNSecs = convert!("days", "hnsecs")(days);
immutable negative = hnsecs < 0;
if (negative)
hnsecs += convert!("hours", "hnsecs")(24);
immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs);
immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs);
hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs);
hnsecs += convert!("hours", "hnsecs")(hour);
hnsecs += convert!("minutes", "hnsecs")(minute);
hnsecs += convert!("seconds", "hnsecs")(second);
if (negative)
hnsecs -= convert!("hours", "hnsecs")(24);
adjTime = daysHNSecs + hnsecs;
}
@safe unittest
{
import std.range : chain;
foreach (second; testMinSecs)
{
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, second),
st.fracSecs,
st.timezone);
st.second = second;
assert(st == expected, format("[%s] [%s]", st, expected));
}
}
auto st = testSysTimesAD[0];
assertThrown!DateTimeException(st.second = -1);
assertThrown!DateTimeException(st.second = 60);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.seconds = 27));
static assert(!__traits(compiles, ist.seconds = 27));
static void testScope(scope ref SysTime st) @safe
{
st.second = 12;
}
}
/++
Fractional seconds past the second (i.e. the portion of a
$(LREF SysTime) which is less than a second).
+/
@property Duration fracSecs() @safe const nothrow scope
{
auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime);
if (hnsecs < 0)
hnsecs += convert!("hours", "hnsecs")(24);
return dur!"hnsecs"(removeUnitsFromHNSecs!"seconds"(hnsecs));
}
///
@safe unittest
{
import core.time : msecs, usecs, hnsecs, nsecs;
import std.datetime.date : DateTime;
auto dt = DateTime(1982, 4, 1, 20, 59, 22);
assert(SysTime(dt, msecs(213)).fracSecs == msecs(213));
assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202));
assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567));
// SysTime and Duration both have a precision of hnsecs (100 ns),
// so nsecs are going to be truncated.
assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700));
}
@safe unittest
{
import std.range : chain;
import core.time;
assert(SysTime(0, UTC()).fracSecs == Duration.zero);
assert(SysTime(1, UTC()).fracSecs == hnsecs(1));
assert(SysTime(-1, UTC()).fracSecs == hnsecs(9_999_999));
foreach (tz; testTZs)
{
foreach (year; chain(testYearsBC, testYearsAD))
{
foreach (md; testMonthDays)
{
foreach (hour; testHours)
{
foreach (minute; testMinSecs)
{
foreach (second; testMinSecs)
{
auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second));
foreach (fs; testFracSecs)
assert(SysTime(dt, fs, tz).fracSecs == fs);
}
}
}
}
}
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.fracSecs == Duration.zero);
assert(ist.fracSecs == Duration.zero);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.fracSecs;
}
}
/++
Fractional seconds past the second (i.e. the portion of a
$(LREF SysTime) which is less than a second).
Params:
fracSecs = The duration to set this $(LREF SysTime)'s fractional
seconds to.
Throws:
$(REF DateTimeException,std,datetime,date) if the given duration
is negative or if it's greater than or equal to one second.
+/
@property void fracSecs(Duration fracSecs) @safe scope
{
enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds."));
enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second."));
auto oldHNSecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(oldHNSecs);
immutable daysHNSecs = convert!("days", "hnsecs")(days);
immutable negative = oldHNSecs < 0;
if (negative)
oldHNSecs += convert!("hours", "hnsecs")(24);
immutable seconds = splitUnitsFromHNSecs!"seconds"(oldHNSecs);
immutable secondsHNSecs = convert!("seconds", "hnsecs")(seconds);
auto newHNSecs = fracSecs.total!"hnsecs" + secondsHNSecs;
if (negative)
newHNSecs -= convert!("hours", "hnsecs")(24);
adjTime = daysHNSecs + newHNSecs;
}
///
@safe unittest
{
import core.time : Duration, msecs, hnsecs, nsecs;
import std.datetime.date : DateTime;
auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22));
assert(st.fracSecs == Duration.zero);
st.fracSecs = msecs(213);
assert(st.fracSecs == msecs(213));
st.fracSecs = hnsecs(1234567);
assert(st.fracSecs == hnsecs(1234567));
// SysTime has a precision of hnsecs (100 ns), so nsecs are
// going to be truncated.
st.fracSecs = nsecs(123456789);
assert(st.fracSecs == hnsecs(1234567));
}
@safe unittest
{
import std.range : chain;
import core.time;
foreach (fracSec; testFracSecs)
{
foreach (st; chain(testSysTimesBC, testSysTimesAD))
{
auto dt = cast(DateTime) st;
auto expected = SysTime(dt, fracSec, st.timezone);
st.fracSecs = fracSec;
assert(st == expected, format("[%s] [%s]", st, expected));
}
}
auto st = testSysTimesAD[0];
assertThrown!DateTimeException(st.fracSecs = hnsecs(-1));
assertThrown!DateTimeException(st.fracSecs = seconds(1));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.fracSecs = msecs(7)));
static assert(!__traits(compiles, ist.fracSecs = msecs(7)));
static void testScope(scope ref SysTime st) @safe
{
st.fracSecs = Duration.zero;
}
}
/++
The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the
internal representation of $(LREF SysTime).
+/
@property long stdTime() @safe const pure nothrow scope @nogc
{
return _stdTime;
}
@safe unittest
{
import core.time;
assert(SysTime(0).stdTime == 0);
assert(SysTime(1).stdTime == 1);
assert(SysTime(-1).stdTime == -1);
assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()).stdTime == 330_000_502L);
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L);
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
assert(cst.stdTime > 0);
assert(ist.stdTime > 0);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.stdTime;
}
}
/++
The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the
internal representation of $(LREF SysTime).
Params:
stdTime = The number of hnsecs since January 1st, 1 A.D. UTC.
+/
@property void stdTime(long stdTime) @safe pure nothrow scope
{
_stdTime = stdTime;
}
@safe unittest
{
import core.time;
static void test(long stdTime, SysTime expected, size_t line = __LINE__)
{
auto st = SysTime(0, UTC());
st.stdTime = stdTime;
assert(st == expected);
}
test(0, SysTime(Date(1, 1, 1), UTC()));
test(1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()));
test(-1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()));
test(330_000_502L, SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()));
test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.stdTime = 27));
static assert(!__traits(compiles, ist.stdTime = 27));
static void testScope(scope ref SysTime st) @safe
{
st.stdTime = 42;
}
}
/++
The current time zone of this $(LREF SysTime). Its internal time is
always kept in UTC, so there are no conversion issues between time zones
due to DST. Functions which return all or part of the time - such as
hours - adjust the time to this $(LREF SysTime)'s time zone before
returning.
+/
@property immutable(TimeZone) timezone() @safe const pure nothrow return scope
{
return _timezone;
}
@safe unittest
{
assert(SysTime.init.timezone is InitTimeZone());
assert(SysTime(DateTime.init, UTC()).timezone is UTC());
static void testScope(scope ref SysTime st) @safe
{
auto result = st.timezone;
}
}
/++
The current time zone of this $(LREF SysTime). It's internal time is
always kept in UTC, so there are no conversion issues between time zones
due to DST. Functions which return all or part of the time - such as
hours - adjust the time to this $(LREF SysTime)'s time zone before
returning.
Params:
timezone = The $(REF _TimeZone,std,datetime,_timezone) to set this
$(LREF SysTime)'s time zone to.
+/
@property void timezone(immutable TimeZone timezone) @safe pure nothrow scope
{
if (timezone is null)
_timezone = LocalTime();
else
_timezone = timezone;
}
@safe unittest
{
SysTime st;
st.timezone = null;
assert(st.timezone is LocalTime());
st.timezone = UTC();
assert(st.timezone is UTC());
static void testScope(scope ref SysTime st) @safe
{
st.timezone = UTC();
}
}
/++
Returns whether DST is in effect for this $(LREF SysTime).
+/
@property bool dstInEffect() @safe const nothrow return scope
{
return _timezone.dstInEffect(_stdTime);
}
// This function's full unit testing is done in the time zone classes, but
// this verifies that SysTime.init works correctly, since historically, it
// has segfaulted due to a null _timezone.
@safe unittest
{
assert(!SysTime.init.dstInEffect);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.dstInEffect;
}
}
/++
Returns what the offset from UTC is for this $(LREF SysTime).
It includes the DST offset in effect at that time (if any).
+/
@property Duration utcOffset() @safe const nothrow return scope
{
return _timezone.utcOffsetAt(_stdTime);
}
// This function's full unit testing is done in the time zone classes, but
// this verifies that SysTime.init works correctly, since historically, it
// has segfaulted due to a null _timezone.
@safe unittest
{
assert(SysTime.init.utcOffset == Duration.zero);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.utcOffset;
}
}
/++
Returns a $(LREF SysTime) with the same std time as this one, but with
$(REF LocalTime,std,datetime,timezone) as its time zone.
+/
SysTime toLocalTime() @safe const pure nothrow scope
{
return SysTime(_stdTime, LocalTime());
}
@safe unittest
{
import core.time;
{
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27));
assert(sysTime == sysTime.toLocalTime());
assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime);
assert(sysTime.toLocalTime().timezone is LocalTime());
assert(sysTime.toLocalTime().timezone is sysTime.timezone);
assert(sysTime.toLocalTime().timezone !is UTC());
}
{
auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60));
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27), stz);
assert(sysTime == sysTime.toLocalTime());
assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime);
assert(sysTime.toLocalTime().timezone is LocalTime());
assert(sysTime.toLocalTime().timezone !is UTC());
assert(sysTime.toLocalTime().timezone !is stz);
}
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toLocalTime();
}
}
/++
Returns a $(LREF SysTime) with the same std time as this one, but with
`UTC` as its time zone.
+/
SysTime toUTC() @safe const pure nothrow scope
{
return SysTime(_stdTime, UTC());
}
@safe unittest
{
import core.time;
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27));
assert(sysTime == sysTime.toUTC());
assert(sysTime._stdTime == sysTime.toUTC()._stdTime);
assert(sysTime.toUTC().timezone is UTC());
assert(sysTime.toUTC().timezone !is LocalTime());
assert(sysTime.toUTC().timezone !is sysTime.timezone);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toUTC();
}
}
/++
Returns a $(LREF SysTime) with the same std time as this one, but with
given time zone as its time zone.
+/
SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow scope
{
if (tz is null)
return SysTime(_stdTime, LocalTime());
else
return SysTime(_stdTime, tz);
}
@safe unittest
{
import core.time;
auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60));
auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27));
assert(sysTime == sysTime.toOtherTZ(stz));
assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime);
assert(sysTime.toOtherTZ(stz).timezone is stz);
assert(sysTime.toOtherTZ(stz).timezone !is LocalTime());
assert(sysTime.toOtherTZ(stz).timezone !is UTC());
assert(sysTime.toOtherTZ(null).timezone is LocalTime());
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toOtherTZ(null);
}
}
/++
Converts this $(LREF SysTime) to unix time (i.e. seconds from midnight,
January 1st, 1970 in UTC).
The C standard does not specify the representation of time_t, so it is
implementation defined. On POSIX systems, unix time is equivalent to
time_t, but that's not necessarily true on other systems (e.g. it is
not true for the Digital Mars C runtime). So, be careful when using unix
time with C functions on non-POSIX systems.
By default, the return type is time_t (which is normally an alias for
int on 32-bit systems and long on 64-bit systems), but if a different
size is required than either int or long can be passed as a template
argument to get the desired size.
If the return type is int, and the result can't fit in an int, then the
closest value that can be held in 32 bits will be used (so `int.max`
if it goes over and `int.min` if it goes under). However, no attempt
is made to deal with integer overflow if the return type is long.
Params:
T = The return type (int or long). It defaults to time_t, which is
normally 32 bits on a 32-bit system and 64 bits on a 64-bit
system.
Returns:
A signed integer representing the unix time which is equivalent to
this SysTime.
+/
T toUnixTime(T = time_t)() @safe const pure nothrow scope
if (is(T == int) || is(T == long))
{
return stdTimeToUnixTime!T(_stdTime);
}
///
@safe unittest
{
import core.time : hours;
import std.datetime.date : DateTime;
import std.datetime.timezone : SimpleTimeZone, UTC;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0);
auto pst = new immutable SimpleTimeZone(hours(-8));
assert(SysTime(DateTime(1970, 1, 1), pst).toUnixTime() == 28800);
auto utc = SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC());
assert(utc.toUnixTime() == 1_198_311_285);
auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst);
assert(ca.toUnixTime() == 1_198_340_085);
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toUnixTime();
}
}
@safe unittest
{
import std.meta : AliasSeq;
import core.time;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0);
static foreach (units; ["hnsecs", "usecs", "msecs"])
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), dur!units(1), UTC()).toUnixTime() == 0);
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1);
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0);
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toUnixTime() == 0);
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toUnixTime() == 0);
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1);
}
/++
Converts from unix time (i.e. seconds from midnight, January 1st, 1970
in UTC) to a $(LREF SysTime).
The C standard does not specify the representation of time_t, so it is
implementation defined. On POSIX systems, unix time is equivalent to
time_t, but that's not necessarily true on other systems (e.g. it is
not true for the Digital Mars C runtime). So, be careful when using unix
time with C functions on non-POSIX systems.
Params:
unixTime = Seconds from midnight, January 1st, 1970 in UTC.
tz = The time zone for the SysTime that's returned.
+/
static SysTime fromUnixTime(long unixTime, immutable TimeZone tz = LocalTime()) @safe pure nothrow
{
return SysTime(unixTimeToStdTime(unixTime), tz);
}
///
@safe unittest
{
import core.time : hours;
import std.datetime.date : DateTime;
import std.datetime.timezone : SimpleTimeZone, UTC;
assert(SysTime.fromUnixTime(0) ==
SysTime(DateTime(1970, 1, 1), UTC()));
auto pst = new immutable SimpleTimeZone(hours(-8));
assert(SysTime.fromUnixTime(28800) ==
SysTime(DateTime(1970, 1, 1), pst));
auto st1 = SysTime.fromUnixTime(1_198_311_285, UTC());
assert(st1 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()));
assert(st1.timezone is UTC());
assert(st1 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst));
auto st2 = SysTime.fromUnixTime(1_198_311_285, pst);
assert(st2 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()));
assert(st2.timezone is pst);
assert(st2 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst));
}
@safe unittest
{
import core.time;
assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC()));
assert(SysTime.fromUnixTime(1) == SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()));
assert(SysTime.fromUnixTime(-1) == SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()));
auto st = SysTime.fromUnixTime(0);
auto dt = cast(DateTime) st;
assert(dt <= DateTime(1970, 2, 1) && dt >= DateTime(1969, 12, 31));
assert(st.timezone is LocalTime());
auto aest = new immutable SimpleTimeZone(hours(10));
assert(SysTime.fromUnixTime(-36000) == SysTime(DateTime(1970, 1, 1), aest));
}
/++
Returns a `timeval` which represents this $(LREF SysTime).
Note that like all conversions in std.datetime, this is a truncating
conversion.
If `timeval.tv_sec` is int, and the result can't fit in an int, then
the closest value that can be held in 32 bits will be used for
`tv_sec`. (so `int.max` if it goes over and `int.min` if it
goes under).
+/
timeval toTimeVal() @safe const pure nothrow scope
{
immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))();
immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L);
immutable tv_usec = cast(typeof(timeval.tv_usec))convert!("hnsecs", "usecs")(fracHNSecs);
return timeval(tv_sec, tv_usec);
}
@safe unittest
{
import core.time;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1));
assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeVal() == timeval(0, 7));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeVal() == timeval(1, 0));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeVal() == timeval(1, 1));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeVal() == timeval(1, 7));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeVal() == timeval(0, 0));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeVal() == timeval(0, -1));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeVal() == timeval(0, -1));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeVal() == timeval(0, -999_001));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983));
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toTimeVal();
}
}
version (StdDdoc)
{
version (Windows) private struct timespec {}
/++
Returns a `timespec` which represents this $(LREF SysTime).
$(BLUE This function is Posix-Only.)
+/
timespec toTimeSpec() @safe const pure nothrow scope;
}
else version (Posix)
{
timespec toTimeSpec() @safe const pure nothrow scope
{
immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))();
immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L);
immutable tv_nsec = cast(typeof(timespec.tv_nsec))convert!("hnsecs", "nsecs")(fracHNSecs);
return timespec(tv_sec, tv_nsec);
}
@safe unittest
{
import core.time;
assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeSpec() == timespec(0, 0));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(0, 900));
assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(0, 1000));
assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeSpec() == timespec(0, 7000));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeSpec() == timespec(1, 0));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(1, 900));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(1, 1000));
assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeSpec() == timespec(1, 7000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeSpec() ==
timespec(0, -100));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeSpec() ==
timespec(0, -1000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeSpec() ==
timespec(0, -1_000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeSpec() ==
timespec(0, -999_001_000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeSpec() ==
timespec(0, -1_000_000));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeSpec() ==
timespec(-1, 0));
assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeSpec() ==
timespec(-1, -999_983_000));
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toTimeSpec();
}
}
}
/++
Returns a `tm` which represents this $(LREF SysTime).
+/
tm toTM() @safe const nothrow scope
{
auto dateTime = cast(DateTime) this;
tm timeInfo;
timeInfo.tm_sec = dateTime.second;
timeInfo.tm_min = dateTime.minute;
timeInfo.tm_hour = dateTime.hour;
timeInfo.tm_mday = dateTime.day;
timeInfo.tm_mon = dateTime.month - 1;
timeInfo.tm_year = dateTime.year - 1900;
timeInfo.tm_wday = dateTime.dayOfWeek;
timeInfo.tm_yday = dateTime.dayOfYear - 1;
timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime);
version (Posix)
{
import std.utf : toUTFz;
timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime);
auto zone = timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName;
timeInfo.tm_zone = zone.toUTFz!(char*)();
}
return timeInfo;
}
@system unittest
{
import std.conv : to;
import core.time;
version (Posix)
{
import std.datetime.timezone : clearTZEnvVar, setTZEnvVar;
setTZEnvVar("America/Los_Angeles");
scope(exit) clearTZEnvVar();
}
{
auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM();
assert(timeInfo.tm_sec == 0);
assert(timeInfo.tm_min == 0);
assert(timeInfo.tm_hour == 0);
assert(timeInfo.tm_mday == 1);
assert(timeInfo.tm_mon == 0);
assert(timeInfo.tm_year == 70);
assert(timeInfo.tm_wday == 4);
assert(timeInfo.tm_yday == 0);
version (Posix)
assert(timeInfo.tm_isdst == 0);
else version (Windows)
assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1);
version (Posix)
{
assert(timeInfo.tm_gmtoff == -8 * 60 * 60);
assert(to!string(timeInfo.tm_zone) == "PST");
}
}
{
auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), hnsecs(15)).toTM();
assert(timeInfo.tm_sec == 7);
assert(timeInfo.tm_min == 15);
assert(timeInfo.tm_hour == 12);
assert(timeInfo.tm_mday == 4);
assert(timeInfo.tm_mon == 6);
assert(timeInfo.tm_year == 110);
assert(timeInfo.tm_wday == 0);
assert(timeInfo.tm_yday == 184);
version (Posix)
assert(timeInfo.tm_isdst == 1);
else version (Windows)
assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1);
version (Posix)
{
assert(timeInfo.tm_gmtoff == -7 * 60 * 60);
assert(to!string(timeInfo.tm_zone) == "PDT");
}
}
// This is more to verify that SysTime.init.toTM() doesn't segfault and
// does something sane rather than that the value is anything
// particularly useful.
{
auto timeInfo = SysTime.init.toTM();
assert(timeInfo.tm_sec == 0);
assert(timeInfo.tm_min == 0);
assert(timeInfo.tm_hour == 0);
assert(timeInfo.tm_mday == 1);
assert(timeInfo.tm_mon == 0);
assert(timeInfo.tm_year == -1899);
assert(timeInfo.tm_wday == 1);
assert(timeInfo.tm_yday == 0);
assert(timeInfo.tm_isdst == 0);
version (Posix)
{
assert(timeInfo.tm_gmtoff == 0);
assert(to!string(timeInfo.tm_zone) == "SysTime.init's timezone");
}
}
static void testScope(scope ref SysTime st) @safe
{
auto result = st.toTM();
}
}
/++
Adds the given number of years or months to this $(LREF SysTime). A
negative number will subtract.
Note that if day overflow is allowed, and the date with the adjusted
year/month overflows the number of days in the new month, then the month
will be incremented by one, and the day set to the number of days
overflowed. (e.g. if the day were 31 and the new month were June, then
the month would be incremented to July, and the new day would be 1). If
day overflow is not allowed, then the day will be set to the last valid
day in the month (e.g. June 31st would become June 30th).
Params:
units = The type of units to add ("years" or "months").
value = The number of months or years to add to this
$(LREF SysTime).
allowOverflow = Whether the days should be allowed to overflow,
causing the month to increment.
+/
ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
if (units == "years" || units == "months")
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
auto date = Date(cast(int) days);
date.add!units(value, allowOverflow);
days = date.dayOfGregorianCal - 1;
if (days < 0)
{
hnsecs -= convert!("hours", "hnsecs")(24);
++days;
}
immutable newDaysHNSecs = convert!("days", "hnsecs")(days);
adjTime = newDaysHNSecs + hnsecs;
return this;
}
@safe unittest
{
auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33));
st1.add!"months"(11);
assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33)));
auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33));
st2.add!"months"(-11);
assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33)));
auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33));
st3.add!"years"(1);
assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33)));
auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33));
st4.add!"years"(1, AllowDayOverflow.no);
assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33)));
}
// Test add!"years"() with AllowDayOverflow.yes
@safe unittest
{
import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"years"(7);
assert(sysTime == SysTime(Date(2006, 7, 6)));
sysTime.add!"years"(-9);
assert(sysTime == SysTime(Date(1997, 7, 6)));
}
{
auto sysTime = SysTime(Date(1999, 2, 28));
sysTime.add!"years"(1);
assert(sysTime == SysTime(Date(2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(2000, 2, 29));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(Date(1999, 3, 1)));
}
{
auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234));
sysTime.add!"years"(7);
assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234)));
sysTime.add!"years"(-9);
assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234)));
}
{
auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207));
sysTime.add!"years"(1);
assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207)));
}
{
auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), usecs(1207)));
}
// Test B.C.
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"years"(-7);
assert(sysTime == SysTime(Date(-2006, 7, 6)));
sysTime.add!"years"(9);
assert(sysTime == SysTime(Date(-1997, 7, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 2, 28));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(Date(-2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(-2000, 2, 29));
sysTime.add!"years"(1);
assert(sysTime == SysTime(Date(-1999, 3, 1)));
}
{
auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234));
sysTime.add!"years"(-7);
assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234)));
sysTime.add!"years"(9);
assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234)));
}
{
auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3)));
}
{
auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3));
sysTime.add!"years"(1);
assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), hnsecs(3)));
}
// Test Both
{
auto sysTime = SysTime(Date(4, 7, 6));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(Date(-1, 7, 6)));
sysTime.add!"years"(5);
assert(sysTime == SysTime(Date(4, 7, 6)));
}
{
auto sysTime = SysTime(Date(-4, 7, 6));
sysTime.add!"years"(5);
assert(sysTime == SysTime(Date(1, 7, 6)));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(Date(-4, 7, 6)));
}
{
auto sysTime = SysTime(Date(4, 7, 6));
sysTime.add!"years"(-8);
assert(sysTime == SysTime(Date(-4, 7, 6)));
sysTime.add!"years"(8);
assert(sysTime == SysTime(Date(4, 7, 6)));
}
{
auto sysTime = SysTime(Date(-4, 7, 6));
sysTime.add!"years"(8);
assert(sysTime == SysTime(Date(4, 7, 6)));
sysTime.add!"years"(-8);
assert(sysTime == SysTime(Date(-4, 7, 6)));
}
{
auto sysTime = SysTime(Date(-4, 2, 29));
sysTime.add!"years"(5);
assert(sysTime == SysTime(Date(1, 3, 1)));
}
{
auto sysTime = SysTime(Date(4, 2, 29));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(Date(-1, 3, 1)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0)));
sysTime.add!"years"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"years"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0));
sysTime.add!"years"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"years"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"years"(-1);
assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329)));
sysTime.add!"years"(5);
assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)));
}
{
auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329));
sysTime.add!"years"(5);
assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329)));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)));
}
{
auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555));
sysTime.add!"years"(5);
assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), msecs(555)));
}
{
auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), msecs(555)));
}
{
auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555));
sysTime.add!"years"(-5).add!"years"(7);
assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), msecs(555)));
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.add!"years"(4)));
static assert(!__traits(compiles, ist.add!"years"(4)));
static void testScope(scope ref SysTime st) @safe
{
auto result = st.add!"years"(42);
}
}
// Test add!"years"() with AllowDayOverflow.no
@safe unittest
{
import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"years"(7, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2006, 7, 6)));
sysTime.add!"years"(-9, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1997, 7, 6)));
}
{
auto sysTime = SysTime(Date(1999, 2, 28));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(2000, 2, 29));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 2, 28)));
}
{
auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234));
sysTime.add!"years"(7, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234)));
sysTime.add!"years"(-9, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234)));
}
{
auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207)));
}
{
auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)));
}
// Test B.C.
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"years"(-7, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2006, 7, 6)));
sysTime.add!"years"(9, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1997, 7, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 2, 28));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(-2000, 2, 29));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 2, 28)));
}
{
auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234));
sysTime.add!"years"(-7, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234)));
sysTime.add!"years"(9, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234)));
}
{
auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3)));
}
{
auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)));
}
// Test Both
{
auto sysTime = SysTime(Date(4, 7, 6));
sysTime.add!"years"(-5, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1, 7, 6)));
sysTime.add!"years"(5, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(4, 7, 6)));
}
{
auto sysTime = SysTime(Date(-4, 7, 6));
sysTime.add!"years"(5, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1, 7, 6)));
sysTime.add!"years"(-5, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-4, 7, 6)));
}
{
auto sysTime = SysTime(Date(4, 7, 6));
sysTime.add!"years"(-8, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-4, 7, 6)));
sysTime.add!"years"(8, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(4, 7, 6)));
}
{
auto sysTime = SysTime(Date(-4, 7, 6));
sysTime.add!"years"(8, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(4, 7, 6)));
sysTime.add!"years"(-8, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-4, 7, 6)));
}
{
auto sysTime = SysTime(Date(-4, 2, 29));
sysTime.add!"years"(5, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1, 2, 28)));
}
{
auto sysTime = SysTime(Date(4, 2, 29));
sysTime.add!"years"(-5, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1, 2, 28)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0)));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"years"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"years"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329));
sysTime.add!"years"(-5);
assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329)));
sysTime.add!"years"(5);
assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)));
}
{
auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329));
sysTime.add!"years"(-5, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329)));
sysTime.add!"years"(5, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)));
}
{
auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329));
sysTime.add!"years"(5, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329)));
sysTime.add!"years"(-5, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)));
}
{
auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555));
sysTime.add!"years"(5, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), msecs(555)));
}
{
auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555));
sysTime.add!"years"(-5, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), msecs(555)));
}
{
auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555));
sysTime.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), msecs(555)));
}
}
// Test add!"months"() with AllowDayOverflow.yes
@safe unittest
{
import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"months"(3);
assert(sysTime == SysTime(Date(1999, 10, 6)));
sysTime.add!"months"(-4);
assert(sysTime == SysTime(Date(1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"months"(6);
assert(sysTime == SysTime(Date(2000, 1, 6)));
sysTime.add!"months"(-6);
assert(sysTime == SysTime(Date(1999, 7, 6)));
}
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"months"(27);
assert(sysTime == SysTime(Date(2001, 10, 6)));
sysTime.add!"months"(-28);
assert(sysTime == SysTime(Date(1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(1999, 5, 31));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(1999, 7, 1)));
}
{
auto sysTime = SysTime(Date(1999, 5, 31));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(Date(1999, 5, 1)));
}
{
auto sysTime = SysTime(Date(1999, 2, 28));
sysTime.add!"months"(12);
assert(sysTime == SysTime(Date(2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(2000, 2, 29));
sysTime.add!"months"(12);
assert(sysTime == SysTime(Date(2001, 3, 1)));
}
{
auto sysTime = SysTime(Date(1999, 7, 31));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(1999, 8, 31)));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(1999, 10, 1)));
}
{
auto sysTime = SysTime(Date(1998, 8, 31));
sysTime.add!"months"(13);
assert(sysTime == SysTime(Date(1999, 10, 1)));
sysTime.add!"months"(-13);
assert(sysTime == SysTime(Date(1998, 9, 1)));
}
{
auto sysTime = SysTime(Date(1997, 12, 31));
sysTime.add!"months"(13);
assert(sysTime == SysTime(Date(1999, 1, 31)));
sysTime.add!"months"(-13);
assert(sysTime == SysTime(Date(1997, 12, 31)));
}
{
auto sysTime = SysTime(Date(1997, 12, 31));
sysTime.add!"months"(14);
assert(sysTime == SysTime(Date(1999, 3, 3)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(Date(1998, 1, 3)));
}
{
auto sysTime = SysTime(Date(1998, 12, 31));
sysTime.add!"months"(14);
assert(sysTime == SysTime(Date(2000, 3, 2)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(Date(1999, 1, 2)));
}
{
auto sysTime = SysTime(Date(1999, 12, 31));
sysTime.add!"months"(14);
assert(sysTime == SysTime(Date(2001, 3, 3)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(Date(2000, 1, 3)));
}
{
auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007));
sysTime.add!"months"(3);
assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007)));
sysTime.add!"months"(-4);
assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007)));
}
{
auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14);
assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), hnsecs(422202)));
}
{
auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14);
assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), hnsecs(422202)));
}
// Test B.C.
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"months"(3);
assert(sysTime == SysTime(Date(-1999, 10, 6)));
sysTime.add!"months"(-4);
assert(sysTime == SysTime(Date(-1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"months"(6);
assert(sysTime == SysTime(Date(-1998, 1, 6)));
sysTime.add!"months"(-6);
assert(sysTime == SysTime(Date(-1999, 7, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"months"(-27);
assert(sysTime == SysTime(Date(-2001, 4, 6)));
sysTime.add!"months"(28);
assert(sysTime == SysTime(Date(-1999, 8, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 5, 31));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(-1999, 7, 1)));
}
{
auto sysTime = SysTime(Date(-1999, 5, 31));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(Date(-1999, 5, 1)));
}
{
auto sysTime = SysTime(Date(-1999, 2, 28));
sysTime.add!"months"(-12);
assert(sysTime == SysTime(Date(-2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(-2000, 2, 29));
sysTime.add!"months"(-12);
assert(sysTime == SysTime(Date(-2001, 3, 1)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 31));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(-1999, 8, 31)));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(-1999, 10, 1)));
}
{
auto sysTime = SysTime(Date(-1998, 8, 31));
sysTime.add!"months"(13);
assert(sysTime == SysTime(Date(-1997, 10, 1)));
sysTime.add!"months"(-13);
assert(sysTime == SysTime(Date(-1998, 9, 1)));
}
{
auto sysTime = SysTime(Date(-1997, 12, 31));
sysTime.add!"months"(13);
assert(sysTime == SysTime(Date(-1995, 1, 31)));
sysTime.add!"months"(-13);
assert(sysTime == SysTime(Date(-1997, 12, 31)));
}
{
auto sysTime = SysTime(Date(-1997, 12, 31));
sysTime.add!"months"(14);
assert(sysTime == SysTime(Date(-1995, 3, 3)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(Date(-1996, 1, 3)));
}
{
auto sysTime = SysTime(Date(-2002, 12, 31));
sysTime.add!"months"(14);
assert(sysTime == SysTime(Date(-2000, 3, 2)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(Date(-2001, 1, 2)));
}
{
auto sysTime = SysTime(Date(-2001, 12, 31));
sysTime.add!"months"(14);
assert(sysTime == SysTime(Date(-1999, 3, 3)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(Date(-2000, 1, 3)));
}
{
auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007));
sysTime.add!"months"(3);
assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007)));
sysTime.add!"months"(-4);
assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007)));
}
{
auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14);
assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), hnsecs(422202)));
}
{
auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14);
assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14);
assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), hnsecs(422202)));
}
// Test Both
{
auto sysTime = SysTime(Date(1, 1, 1));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(Date(0, 12, 1)));
sysTime.add!"months"(1);
assert(sysTime == SysTime(Date(1, 1, 1)));
}
{
auto sysTime = SysTime(Date(4, 1, 1));
sysTime.add!"months"(-48);
assert(sysTime == SysTime(Date(0, 1, 1)));
sysTime.add!"months"(48);
assert(sysTime == SysTime(Date(4, 1, 1)));
}
{
auto sysTime = SysTime(Date(4, 3, 31));
sysTime.add!"months"(-49);
assert(sysTime == SysTime(Date(0, 3, 2)));
sysTime.add!"months"(49);
assert(sysTime == SysTime(Date(4, 4, 2)));
}
{
auto sysTime = SysTime(Date(4, 3, 31));
sysTime.add!"months"(-85);
assert(sysTime == SysTime(Date(-3, 3, 3)));
sysTime.add!"months"(85);
assert(sysTime == SysTime(Date(4, 4, 3)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0)));
sysTime.add!"months"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"months"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0));
sysTime.add!"months"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"months"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17));
sysTime.add!"months"(-1);
assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17)));
sysTime.add!"months"(1);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)));
}
{
auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9));
sysTime.add!"months"(-85);
assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), msecs(9)));
sysTime.add!"months"(85);
assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), msecs(9)));
}
{
auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9));
sysTime.add!"months"(85);
assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), msecs(9)));
sysTime.add!"months"(-85);
assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9)));
}
{
auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9));
sysTime.add!"months"(85).add!"months"(-83);
assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9)));
}
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
static assert(!__traits(compiles, cst.add!"months"(4)));
static assert(!__traits(compiles, ist.add!"months"(4)));
static void testScope(scope ref SysTime st) @safe
{
auto result = st.add!"months"(42);
}
}
// Test add!"months"() with AllowDayOverflow.no
@safe unittest
{
import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"months"(3, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 10, 6)));
sysTime.add!"months"(-4, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"months"(6, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2000, 1, 6)));
sysTime.add!"months"(-6, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 7, 6)));
}
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.add!"months"(27, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2001, 10, 6)));
sysTime.add!"months"(-28, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(1999, 5, 31));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 6, 30)));
}
{
auto sysTime = SysTime(Date(1999, 5, 31));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 4, 30)));
}
{
auto sysTime = SysTime(Date(1999, 2, 28));
sysTime.add!"months"(12, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(2000, 2, 29));
sysTime.add!"months"(12, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2001, 2, 28)));
}
{
auto sysTime = SysTime(Date(1999, 7, 31));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 8, 31)));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 9, 30)));
}
{
auto sysTime = SysTime(Date(1998, 8, 31));
sysTime.add!"months"(13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 9, 30)));
sysTime.add!"months"(-13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1998, 8, 30)));
}
{
auto sysTime = SysTime(Date(1997, 12, 31));
sysTime.add!"months"(13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 1, 31)));
sysTime.add!"months"(-13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1997, 12, 31)));
}
{
auto sysTime = SysTime(Date(1997, 12, 31));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 2, 28)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1997, 12, 28)));
}
{
auto sysTime = SysTime(Date(1998, 12, 31));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2000, 2, 29)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1998, 12, 29)));
}
{
auto sysTime = SysTime(Date(1999, 12, 31));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(2001, 2, 28)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1999, 12, 28)));
}
{
auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007));
sysTime.add!"months"(3, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007)));
sysTime.add!"months"(-4, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007)));
}
{
auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), hnsecs(422202)));
}
{
auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202)));
}
// Test B.C.
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"months"(3, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 10, 6)));
sysTime.add!"months"(-4, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"months"(6, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1998, 1, 6)));
sysTime.add!"months"(-6, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 7, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.add!"months"(-27, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2001, 4, 6)));
sysTime.add!"months"(28, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 8, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 5, 31));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 6, 30)));
}
{
auto sysTime = SysTime(Date(-1999, 5, 31));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 4, 30)));
}
{
auto sysTime = SysTime(Date(-1999, 2, 28));
sysTime.add!"months"(-12, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2000, 2, 28)));
}
{
auto sysTime = SysTime(Date(-2000, 2, 29));
sysTime.add!"months"(-12, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2001, 2, 28)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 31));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 8, 31)));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 9, 30)));
}
{
auto sysTime = SysTime(Date(-1998, 8, 31));
sysTime.add!"months"(13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1997, 9, 30)));
sysTime.add!"months"(-13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1998, 8, 30)));
}
{
auto sysTime = SysTime(Date(-1997, 12, 31));
sysTime.add!"months"(13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1995, 1, 31)));
sysTime.add!"months"(-13, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1997, 12, 31)));
}
{
auto sysTime = SysTime(Date(-1997, 12, 31));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1995, 2, 28)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1997, 12, 28)));
}
{
auto sysTime = SysTime(Date(-2002, 12, 31));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2000, 2, 29)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2002, 12, 29)));
}
{
auto sysTime = SysTime(Date(-2001, 12, 31));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-1999, 2, 28)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-2001, 12, 28)));
}
{
auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007));
sysTime.add!"months"(3, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007)));
sysTime.add!"months"(-4, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007)));
}
{
auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), hnsecs(422202)));
}
{
auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.add!"months"(14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), hnsecs(422202)));
sysTime.add!"months"(-14, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202)));
}
// Test Both
{
auto sysTime = SysTime(Date(1, 1, 1));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(0, 12, 1)));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(1, 1, 1)));
}
{
auto sysTime = SysTime(Date(4, 1, 1));
sysTime.add!"months"(-48, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(0, 1, 1)));
sysTime.add!"months"(48, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(4, 1, 1)));
}
{
auto sysTime = SysTime(Date(4, 3, 31));
sysTime.add!"months"(-49, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(0, 2, 29)));
sysTime.add!"months"(49, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(4, 3, 29)));
}
{
auto sysTime = SysTime(Date(4, 3, 31));
sysTime.add!"months"(-85, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(-3, 2, 28)));
sysTime.add!"months"(85, AllowDayOverflow.no);
assert(sysTime == SysTime(Date(4, 3, 28)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0)));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0)));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0)));
}
{
auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)));
}
{
auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17));
sysTime.add!"months"(-1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17)));
sysTime.add!"months"(1, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)));
}
{
auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9));
sysTime.add!"months"(-85, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), msecs(9)));
sysTime.add!"months"(85, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), msecs(9)));
}
{
auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9));
sysTime.add!"months"(85, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), msecs(9)));
sysTime.add!"months"(-85, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9)));
}
{
auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9));
sysTime.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9)));
}
}
/++
Adds the given number of years or months to this $(LREF SysTime). A
negative number will subtract.
The difference between rolling and adding is that rolling does not
affect larger units. Rolling a $(LREF SysTime) 12 months
gets the exact same $(LREF SysTime). However, the days can still be
affected due to the differing number of days in each month.
Because there are no units larger than years, there is no difference
between adding and rolling years.
Params:
units = The type of units to add ("years" or "months").
value = The number of months or years to add to this
$(LREF SysTime).
allowOverflow = Whether the days should be allowed to overflow,
causing the month to increment.
+/
ref SysTime roll(string units)
(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
if (units == "years")
{
return add!"years"(value, allowOverflow);
}
///
@safe unittest
{
import std.datetime.date : AllowDayOverflow, DateTime;
auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33));
st1.roll!"months"(1);
assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33)));
auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33));
st2.roll!"months"(-1);
assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33)));
auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33));
st3.roll!"months"(1);
assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33)));
auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33));
st4.roll!"months"(1, AllowDayOverflow.no);
assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33)));
auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33));
st5.roll!"years"(1);
assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33)));
auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33));
st6.roll!"years"(1, AllowDayOverflow.no);
assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33)));
}
@safe unittest
{
auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33));
st.roll!"years"(4);
static assert(!__traits(compiles, cst.roll!"years"(4)));
static assert(!__traits(compiles, ist.roll!"years"(4)));
static void testScope(scope ref SysTime st) @safe
{
auto result = st.roll!"years"(42);
}
}
// Shares documentation with "years" overload.
ref SysTime roll(string units)
(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow scope
if (units == "months")
{
auto hnsecs = adjTime;
auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1;
if (hnsecs < 0)
{
hnsecs += convert!("hours", "hnsecs")(24);
--days;
}
auto date = Date(cast(int) days);
date.roll!"months"(value, allowOverflow);
days = date.dayOfGregorianCal - 1;
if (days < 0)
{
hnsecs -= convert!("hours", "hnsecs")(24);
++days;
}
immutable newDaysHNSecs = convert!("days", "hnsecs")(days);
adjTime = newDaysHNSecs + hnsecs;
return this;
}
// Test roll!"months"() with AllowDayOverflow.yes
@safe unittest
{
import core.time;
// Test A.D.
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.roll!"months"(3);
assert(sysTime == SysTime(Date(1999, 10, 6)));
sysTime.roll!"months"(-4);
assert(sysTime == SysTime(Date(1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.roll!"months"(6);
assert(sysTime == SysTime(Date(1999, 1, 6)));
sysTime.roll!"months"(-6);
assert(sysTime == SysTime(Date(1999, 7, 6)));
}
{
auto sysTime = SysTime(Date(1999, 7, 6));
sysTime.roll!"months"(27);
assert(sysTime == SysTime(Date(1999, 10, 6)));
sysTime.roll!"months"(-28);
assert(sysTime == SysTime(Date(1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(1999, 5, 31));
sysTime.roll!"months"(1);
assert(sysTime == SysTime(Date(1999, 7, 1)));
}
{
auto sysTime = SysTime(Date(1999, 5, 31));
sysTime.roll!"months"(-1);
assert(sysTime == SysTime(Date(1999, 5, 1)));
}
{
auto sysTime = SysTime(Date(1999, 2, 28));
sysTime.roll!"months"(12);
assert(sysTime == SysTime(Date(1999, 2, 28)));
}
{
auto sysTime = SysTime(Date(2000, 2, 29));
sysTime.roll!"months"(12);
assert(sysTime == SysTime(Date(2000, 2, 29)));
}
{
auto sysTime = SysTime(Date(1999, 7, 31));
sysTime.roll!"months"(1);
assert(sysTime == SysTime(Date(1999, 8, 31)));
sysTime.roll!"months"(1);
assert(sysTime == SysTime(Date(1999, 10, 1)));
}
{
auto sysTime = SysTime(Date(1998, 8, 31));
sysTime.roll!"months"(13);
assert(sysTime == SysTime(Date(1998, 10, 1)));
sysTime.roll!"months"(-13);
assert(sysTime == SysTime(Date(1998, 9, 1)));
}
{
auto sysTime = SysTime(Date(1997, 12, 31));
sysTime.roll!"months"(13);
assert(sysTime == SysTime(Date(1997, 1, 31)));
sysTime.roll!"months"(-13);
assert(sysTime == SysTime(Date(1997, 12, 31)));
}
{
auto sysTime = SysTime(Date(1997, 12, 31));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(Date(1997, 3, 3)));
sysTime.roll!"months"(-14);
assert(sysTime == SysTime(Date(1997, 1, 3)));
}
{
auto sysTime = SysTime(Date(1998, 12, 31));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(Date(1998, 3, 3)));
sysTime.roll!"months"(-14);
assert(sysTime == SysTime(Date(1998, 1, 3)));
}
{
auto sysTime = SysTime(Date(1999, 12, 31));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(Date(1999, 3, 3)));
sysTime.roll!"months"(-14);
assert(sysTime == SysTime(Date(1999, 1, 3)));
}
{
auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007));
sysTime.roll!"months"(3);
assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007)));
sysTime.roll!"months"(-4);
assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007)));
}
{
auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), hnsecs(422202)));
sysTime.roll!"months"(-14);
assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), hnsecs(422202)));
}
{
auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), hnsecs(422202)));
sysTime.roll!"months"(-14);
assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), hnsecs(422202)));
}
// Test B.C.
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.roll!"months"(3);
assert(sysTime == SysTime(Date(-1999, 10, 6)));
sysTime.roll!"months"(-4);
assert(sysTime == SysTime(Date(-1999, 6, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.roll!"months"(6);
assert(sysTime == SysTime(Date(-1999, 1, 6)));
sysTime.roll!"months"(-6);
assert(sysTime == SysTime(Date(-1999, 7, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 6));
sysTime.roll!"months"(-27);
assert(sysTime == SysTime(Date(-1999, 4, 6)));
sysTime.roll!"months"(28);
assert(sysTime == SysTime(Date(-1999, 8, 6)));
}
{
auto sysTime = SysTime(Date(-1999, 5, 31));
sysTime.roll!"months"(1);
assert(sysTime == SysTime(Date(-1999, 7, 1)));
}
{
auto sysTime = SysTime(Date(-1999, 5, 31));
sysTime.roll!"months"(-1);
assert(sysTime == SysTime(Date(-1999, 5, 1)));
}
{
auto sysTime = SysTime(Date(-1999, 2, 28));
sysTime.roll!"months"(-12);
assert(sysTime == SysTime(Date(-1999, 2, 28)));
}
{
auto sysTime = SysTime(Date(-2000, 2, 29));
sysTime.roll!"months"(-12);
assert(sysTime == SysTime(Date(-2000, 2, 29)));
}
{
auto sysTime = SysTime(Date(-1999, 7, 31));
sysTime.roll!"months"(1);
assert(sysTime == SysTime(Date(-1999, 8, 31)));
sysTime.roll!"months"(1);
assert(sysTime == SysTime(Date(-1999, 10, 1)));
}
{
auto sysTime = SysTime(Date(-1998, 8, 31));
sysTime.roll!"months"(13);
assert(sysTime == SysTime(Date(-1998, 10, 1)));
sysTime.roll!"months"(-13);
assert(sysTime == SysTime(Date(-1998, 9, 1)));
}
{
auto sysTime = SysTime(Date(-1997, 12, 31));
sysTime.roll!"months"(13);
assert(sysTime == SysTime(Date(-1997, 1, 31)));
sysTime.roll!"months"(-13);
assert(sysTime == SysTime(Date(-1997, 12, 31)));
}
{
auto sysTime = SysTime(Date(-1997, 12, 31));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(Date(-1997, 3, 3)));
sysTime.roll!"months"(-14);
assert(sysTime == SysTime(Date(-1997, 1, 3)));
}
{
auto sysTime = SysTime(Date(-2002, 12, 31));
sysTime.roll!"months"(14);
assert(sysTime == SysTime(Date(-2002, 3, 3)));
sysTime.roll!"months"(-14);