diff --git a/phobos.d b/phobos.d index 3c23c91a6..81d8832b9 100644 --- a/phobos.d +++ b/phobos.d @@ -148,6 +148,9 @@ D language compiler. Also, check out the
std.date
Date and time functions. Support locales. +
std.datetime +
Date and time-related types and functions. +
std.file
Basic file operations like read, write, append. diff --git a/std.ddoc b/std.ddoc index 5295a18e7..984cfa57d 100644 --- a/std.ddoc +++ b/std.ddoc @@ -167,6 +167,7 @@ NAVIGATION_PHOBOS= $(LI std.cpuid) $(LI std.ctype) $(LI std.date) + $(LI std.datetime) $(LI std.demangle) $(LI std.encoding) $(LI std.exception) diff --git a/std/datetime.d b/std/datetime.d index a4e288a40..b9a2b62df 100644 --- a/std/datetime.d +++ b/std/datetime.d @@ -1,500 +1,29485 @@ -/******************************************************************************* - * Platform-independent high precision StopWatch. - * - * This module provide: - * $(UL - * $(LI StopWatch) - * $(LI Benchmarks) - * $(LI Some helper functions) - * ) - * - * - * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Kato Shoichi - */ +//Written in the D programming language + +/++ + Module containing Date/Time functionality. + + This module provides: + $(UL + $(LI Types to represent points in time: SysTime, Date, TimeOfDay, and DateTime.) + $(LI Types to represent durations of time.) + $(LI Types to represent intervals of time.) + $(LI Types to represent ranges over intervals of time.) + $(LI Types to represent time zones (used by SysTime).) + $(LI A platform-independent, high precision stop watch type: StopWatch) + $(LI Benchmarking functions.) + $(LI Various helper functions.) + ) + + Three of the main concepts used in this module are time points, time durations, + and time intervals. + + A time point is a specific point in time. e.g. January 5th, 2010 + or 5:00. + + A time duration is a length of time with units. e.g. 5 days or 231 seconds. + + A time interval indicates a period of time associated with a fixed point in time. + So, it is either two time points associated with each other, indicating the time + starting at the first point up to, but not including, the second point - e.g. + [January 5th, 2010 - March 10th, 2010$(RPAREN) - or it is a time point and a time + duration associated with one another. e.g. January 5th, 2010 and 5 days, + indicating [January 5th, 2010 - January 10th, 2010$(RPAREN). + + Various arithmetic operations are supported between time points and durations + (e.g. the difference between two time points is a time duration), and ranges + can be gotten from time intervals, so range-based operations may be done on a + series of time points. + + The types that the typical user is most likely to be interested in are Date + (if they want dates but don't care about time), DateTime (if they want dates + and times but don't care about time zones), SysTime (if they want the date + and time from the OS and/or do care about time zones), and StopWatch (a + platform-independent, high precision stop watch). Date and DateTime are + optimized for calendar-based operations, while SysTime is designed for dealing + with time from the OS. Check out their specific documentation for more details. + + To get the current time, use $(D Clock.currTime()). It will return the current + time as a SysTime. If you want to print it, simple $(D toString()) will do, + but if you use one of $(D toISOString()), $(D toISOExtendedString()), or + $(D toSimpleString()), you can use the corresponding $(D fromISOString()), + $(D fromISOExtendedString()), or $(D fromISOExtendedString()) to create a + $(D SysTime) from the string. + + Examples: +-------------------- +auto currentTime = Clock.currTime(); +auto timeString = currentTime.toISOExtendedString(); +auto restoredTime = SysTime.fromISOExtendedString(timeString); +-------------------- + + Various functions take a string (or strings) to represent a unit of time + (e.g. $(D convert!("days", "hours")(numDays))). The valid strings to use + with such functions are "years", "months", "weeks", "days", "hours", + "minutes", "seconds", "msecs" (milliseconds), "usecs" (microseconds), + "hnsecs" (hecto-nanoseconds - i.e. 100 ns) or some subset thereof. There + are a few functions in core.time which take "nsecs", but because nothing + in std.datetime has precision greater than hnsecs, and very little in + core.time does, no functions in std.datetime accept "nsecs". + + Note: + core.time is publicly imported by std.datetime, so if you're using + std.datetime, you don't need to import core.time. Also, DateTimeException + is an alias for core.time's TimeException, so you don't need to worry + about core.time functions and std.datetime functions throwing a different + type of exception (except in the rare case that they throw something other + than TimeException or DateTimeException). + + See_Also: + ISO 8601 + Wikipedia entry on TZ database + List of Time Zones + + Copyright: Copyright 2010 + License: Boost License 1.0. + Authors: Jonathan M Davis and Kato Shoichi ++/ module std.datetime; -@safe: +public import core.time; -import std.traits, std.exception, std.functional; +import core.exception; +import core.stdc.time; -version (Windows) +import std.array; +import std.algorithm; +import std.conv; +import std.ctype; +import std.exception; +import std.file; +import std.functional; +import std.math; +import std.metastrings; +import std.path; +import std.range; +import std.stdio; +import std.string; +import std.system; +import std.traits; + +version(Windows) { - import core.sys.windows.windows; - import std.windows.syserror; +import core.sys.windows.windows; +import std.c.windows.winsock; + +//For system call to access the registry. +pragma(lib, "advapi32.lib"); } -else version (Posix) +else version(Posix) { - import core.sys.posix.time; - import core.sys.posix.sys.time; +import core.sys.posix.arpa.inet; +import core.sys.posix.stdlib; +import core.sys.posix.time; +import core.sys.posix.sys.time; } - -//############################################################################## -//############################################################################## -//### -//### StopWatch -//### -//############################################################################## -//############################################################################## - - -/******************************************************************************* - * System clock time. - * - * This type maintains the most high precision ticks of system clock in each - * environment. - * (For StopWatch) - */ -struct Ticks +version(unittest) { -@safe:// @@@BUG@@@ workaround for bug 4211 - - - /*************************************************************************** - * Ticks that is counted per 1[s]. - * - * Confirm that it is not 0, to examine whether you can use Ticks. - */ - static immutable long ticksPerSec; - - - /*************************************************************************** - * Ticks when application begins. - */ - static immutable Ticks appOrigin; - - - @trusted - shared static this() - { - version (Windows) - { - if (QueryPerformanceFrequency(cast(long*)&ticksPerSec) == 0) - { - ticksPerSec = 0; - } - } - else version (Posix) - { - static if (is(typeof(clock_gettime)==function)) - { - timespec ts; - if ( clock_getres(CLOCK_REALTIME, &ts) != 0) - { - ticksPerSec = 0; - } - else - { - ticksPerSec = 1000_000_000 / ts.tv_nsec; - } - } - else - { - ticksPerSec = 1_000_000; - } - } - if (ticksPerSec != 0) - { - appOrigin = systime(); - } - } - - - unittest - { - assert(ticksPerSec); - } - - - /*************************************************************************** - * Unknown value for Ticks - * - * You can convert this value into number of seconds by dividing it - * by ticksPerSec. - */ - long value; - - - /*************************************************************************** - * [s] as integer or real number - * - * Attention: This method truncate the number of digits after decimal point. - */ - const - T toSeconds(T)() if (isIntegral!T && T.sizeof >= 4) - { - return cast(T)(value/ticksPerSec); - } - - - /// ditto - const - T toSeconds(T)() if (isFloatingPoint!T) - { - //@@@BUG@@@ workaround for bug 4689 - long t = ticksPerSec; - return value/cast(T)t; - } - - - /*************************************************************************** - * [s] as real number - */ - @property alias toSeconds!real seconds; - - - /*************************************************************************** - * [s] as integer - */ - @property alias toSeconds!long sec; - - - - unittest - { - auto t = Ticks(ticksPerSec); - assert(t.sec == 1); - t = Ticks(ticksPerSec-1); - assert(t.sec == 0); - t = Ticks(ticksPerSec*2); - assert(t.sec == 2); - t = Ticks(ticksPerSec*2-1); - assert(t.sec == 1); - t = Ticks(-1); - assert(t.sec == 0); - t = Ticks(-ticksPerSec-1); - assert(t.sec == -1); - t = Ticks(-ticksPerSec); - assert(t.sec == -1); - } - - - /*************************************************************************** - * Create Ticks from [s] as integer - */ - static Ticks fromSeconds(T)(T sec) if (isNumeric!T) - { - return Ticks(cast(long)(sec * ticksPerSec)); - } - - - unittest - { - auto t = Ticks.fromSeconds(1000000); - assert(t.sec == 1000000); - t = Ticks.fromSeconds(2000000); - assert(t.sec == 2000000); - t.value -= 1; - assert(t.sec == 1999999); - } - - - /*************************************************************************** - * [ms] as integer or real number - */ - const - T toMilliseconds(T)() if (isIntegral!T && T.sizeof >= 4) - { - return value/(ticksPerSec/1000); - } - - - /// ditto - const - T toMilliseconds(T)() if (isFloatingPoint!T) - { - return toSeconds!T * 1000; - } - - /*************************************************************************** - * [ms] as real number - */ - @property alias toMilliseconds!real milliseconds; - - - /*************************************************************************** - * [ms] as integer - */ - @property alias toMilliseconds!long msec; - - - /*************************************************************************** - * Create Ticks from [ms] as integer - */ - static Ticks fromMilliseconds(long msec) - { - return Ticks(msec*(ticksPerSec/1000)); - } - - - unittest - { - auto t = Ticks.fromMilliseconds(1000000); - assert(t.msec == 1000000); - t = Ticks.fromMilliseconds(2000000); - assert(t.msec == 2000000); - t.value -= 1; - assert(t.msec == 1999999); - } - - - /*************************************************************************** - * [us] as integer or real number - */ - const - T toMicroseconds(T)() if (isIntegral!T && T.sizeof >= 4) - { - return value/(ticksPerSec/1000/1000); - } - - - /// ditto - const - T toMicroseconds(T)() if (isFloatingPoint!T) - { - return toMilliseconds!T * 1000; - } - - - /*************************************************************************** - * [us] as real number - */ - @property alias toMicroseconds!real microseconds; - - - /*************************************************************************** - * [us] as integer - */ - alias toMicroseconds!long usec; - - - /*************************************************************************** - * Create Ticks from [us] as integer - */ - static Ticks fromMicroseconds(long usec) - { - return Ticks(usec*(ticksPerSec/1000/1000)); - } - - - unittest - { - auto t = Ticks.fromMicroseconds(1000000); - assert(t.usec == 1000000); - t = Ticks.fromMicroseconds(2000000); - assert(t.usec == 2000000); - t.value -= 1; - assert(t.usec == 1999999); - } - - - /*************************************************************************** - * operator overroading "-=, +=" - * - * BUG: This should be return "ref Ticks", but bug2460 prevents that. - */ - void opOpAssign(string op)(in Ticks t) if (op == "+" || op == "-") - { - mixin("value "~op~"= t.value;"); - //return this; - } - - - unittest - { - Ticks a = systime(), b = systime(); - a += systime(); - assert(a.seconds >= 0); - b -= systime(); - assert(b.seconds <= 0); - } - - - /*************************************************************************** - * operator overroading "-, +" - */ - const - Ticks opBinary(string op)(in Ticks t) if (op == "-" || op == "+") - { - Ticks lhs = this; - lhs.opOpAssign!op(t); - return lhs; - } - - - unittest - { - auto a = systime(); - auto b = systime(); - assert((a + b).seconds > 0); - assert((a - b).seconds <= 0); - } - - - /*************************************************************************** - * operator overroading "==" - */ - const - equals_t opEquals(ref const Ticks t) - { - return value == t.value; - } - - - unittest - { - auto t1 = systime(); - assert(t1 == t1); - } - - - /*************************************************************************** - * operator overroading "<, >, <=, >=" - */ - const - int opCmp(ref const Ticks t) - { - return value < t.value? -1: value == t.value ? 0 : 1; - } - - - unittest - { - auto t1 = systime(); - auto t2 = systime(); - assert(t1 <= t2); - assert(t2 >= t1); - } - - - /*************************************************************************** - * operator overroading "*=, /=" - */ - void opOpAssign(string op, T)(T x) - if ((op == "*" || op == "/") && isNumeric!(T)) - { - mixin("value "~op~"= x;"); - } - - - unittest - { - immutable t = systime(); - // * - { - Ticks t1 = t, t2 = t; - t1 *= 2; - assert(t < t1); - t2 *= 2.1L; - assert(t2 > t1); - } - // / - { - Ticks t1 = t, t2 = t; - t1 /= 2; - assert(t1 < t); - t2 /= 2.1L; - assert(t2 < t1); - } - } - - - /*************************************************************************** - * operator overroading "*", "/" - */ - Ticks opBinary(string op, T)(T x) - if ((op == "*" || op == "/") && isNumeric!(T)) - { - auto lhs = this; - lhs.opOpAssign!op(x); - return lhs; - } - - - unittest - { - auto t = systime(); - auto t2 = t*2; - assert(t < t2); - assert(t*3.5 > t2); - } - - - /*************************************************************************** - * operator overroading "/" - */ - const - real opBinary(string op)(Ticks x) if (op == "/") - { - return value / cast(real)x.value; - } - - - unittest - { - auto t = systime(); - assert(t/systime() <= 1); - } +import std.c.string; } -/*************************************************************************** - * Special type for constructor - */ -enum AutoStart +alias std.string.indexOf indexOf; +alias std.math.abs abs; + + +//Note: There various functions which void as their return type and ref of the +// struct type which they're in as a commented out return type. Ideally, +// they would return the ref, but there are several dmd bugs which prevent +// that, relating to both ref and invariants. So, I've left the ref return +// types commented out with the idea that those functions can be made to +// return a ref to this once those bugs have been fixed. + + +//============================================================================== +// Section with public enums and constants. +//============================================================================== + +/++ + Represents the 12 months of the Gregorian year (January is 1). + +/ +enum Month : ubyte { jan = 1, /// + feb, /// + mar, /// + apr, /// + may, /// + jun, /// + jul, /// + aug, /// + sep, /// + oct, /// + nov, /// + dec /// + } + + +/++ + Represents the 7 days of the Gregorian week (Sunday is 0). + +/ +enum DayOfWeek : ubyte { sun = 0, /// + mon, /// + tue, /// + wed, /// + thu, /// + fri, /// + sat /// + } + + +/++ + In some date calculations, adding months or years can cause the date to fall on + a day of the month which is not valid (e.g. February 29th 2001 or June 31st 2000). + If overflow is allowed (as is the default), then the month will be incremented + accordingly (so, February 29th 2001 would become March 1st 2001, and June 31st 2000 + would become July 1st 2000). If overflow is not allowed, then the day will be + adjusted to the last valid day in that month (so, February 29th 2001 would become + February 28th 2001 and June 31st 2000 would become June 30th 2000). + + AllowDayOverflow only applies to calculations involving months or years. + +/ +enum AllowDayOverflow { - /// + /// No, don't allow day overflow. no, - /// + + /// Yes, allow day overflow. yes } +/++ + Indicates a direction in time. One example of its use is $(D Interval)'s + $(D expand()) function which uses it to indicate whether the interval should + be expanded backwards (into the past), forwards (into the future), or both. + +/ +enum Direction +{ + /// Backward. + bwd, -/******************************************************************************* - * StopWatch's AutoStart flag - */ + /// Forward. + fwd, + + /// Both backward and forward. + both +} + + +/++ + Used to indicate whether $(D popFront()) should be called immediately upon + creating a range. The idea is that for some functions used to generate a + range for an interval, $(D front) is not necessarily a time point which would + ever be generated by the range, and if you want the first time point in the + range to match what the function generates, then you use $(D PopFirst.yes) + to indicate that the range should have $(D popFront()) called on it before the + range is returned so that $(D front) is a time point which the function would + generate. + + For instance, if the function used to generate a range of time points generated + successive Easters (i.e. you're iterating over all of the Easters within the + interval), the initial date probably isn't an Easter. By using $(D PopFirst.yes), + you would be telling the function which returned the range that you wanted + $(D popFront()) to be called so that front would then be an Easter - the next + one generated by the function (which if you were iterating forward, would be + the Easter following the original $(D front), while if you were iterating backward, + it would be the Easter prior to the original $(D front)). If $(D PopFirst.no) + were used, then $(D front) would remain the original time point and it would + not necessarily be a time point which would be generated by the range-generating + function (which in many cases is exactly what you want - e.g. if you were + iterating over every day starting at the beginning of the interval). + +/ +enum PopFirst +{ + /// No, don't call popFront() before returning the range. + no, + + /// Yes, call popFront() before returning the range. + yes +} + + +/++ + Used by StopWatch to indicate whether it should start immediately upon construction. + +/ +enum AutoStart +{ + /// No, don't start the StopWatch when it is constructed. + no, + + /// Yes, do start the StopWatch when it is constructed. + yes +} + + +/++ + Array of the strings representing time units, starting with the smallest unit + and going to the largest. It does not include "nsecs". + + "hnsecs" (hecto-nanoseconds (100 ns)), "usecs" (microseconds), "msecs" (milliseconds), + "seconds", "minutes", "hours", "days", "weeks", "months", "years" + +/ +immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", + "hours", "days", "weeks", "months", "years"]; + + +//============================================================================== +// Section with time points. +//============================================================================== + +/++ + Represents a date in the Proleptic Gregorian Calendar ranging from 32,768 B.C. + to 32,767 A.D. Positive years are A.D. Non-positive years are B.C. + + Year, month, and day are kept separately internally so that Date is optimized for + calendar operations. + + Date uses the Proleptic Gregorian Calendar, so it assumes the Gregorian leap year + calculations for its entire length. And, as per ISO 8601, it also treats 1 B.C. as + year 0, so 1 B.C. is 0, 2 B.C. is -1, etc. Use $(D yearBC) if want B.C. as a positive + integer with 1 B.C. being the year prior to 1 A.D. + + Year 0 is a leap year. + +/ +struct Date +{ +public: + + /++ + Throws: + DateTimeException if the resulting Date would not be valid. + + Params: + year = Year of the Gregorian Calendar. Positive values are A.D. + Non-positive values are B.C. with year 0 being the year + prior to 1 A.D. + month = Month of the year. + day = Day of the month. + +/ + this(int year, int month, int day) pure + { + enforceValid!"months"(cast(Month)month); + enforceValid!"days"(year, cast(Month)month, day); + + _year = cast(short)year; + _month = cast(Month)month; + _day = cast(ubyte)day; + } + + unittest + { + assertPred!"=="(Date(1, 1, 1), Date.init); + + static void testDate(in Date date, int year, int month, int day, size_t line = __LINE__) + { + assertPred!"=="(date._year, year, "", __FILE__, line); + assertPred!"=="(date._month, month, "", __FILE__, line); + assertPred!"=="(date._day, day, "", __FILE__, line); + } + + testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); + testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); + testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); + + //Test A.D. + assertThrown!DateTimeException(Date(1, 0, 1)); + assertThrown!DateTimeException(Date(1, 1, 0)); + assertThrown!DateTimeException(Date(1999, 13, 1)); + assertThrown!DateTimeException(Date(1999, 1, 32)); + assertThrown!DateTimeException(Date(1999, 2, 29)); + assertThrown!DateTimeException(Date(2000, 2, 30)); + assertThrown!DateTimeException(Date(1999, 3, 32)); + assertThrown!DateTimeException(Date(1999, 4, 31)); + assertThrown!DateTimeException(Date(1999, 5, 32)); + assertThrown!DateTimeException(Date(1999, 6, 31)); + assertThrown!DateTimeException(Date(1999, 7, 32)); + assertThrown!DateTimeException(Date(1999, 8, 32)); + assertThrown!DateTimeException(Date(1999, 9, 31)); + assertThrown!DateTimeException(Date(1999, 10, 32)); + assertThrown!DateTimeException(Date(1999, 11, 31)); + assertThrown!DateTimeException(Date(1999, 12, 32)); + + assertNotThrown!DateTimeException(Date(1999, 1, 31)); + assertNotThrown!DateTimeException(Date(1999, 2, 28)); + assertNotThrown!DateTimeException(Date(2000, 2, 29)); + assertNotThrown!DateTimeException(Date(1999, 3, 31)); + assertNotThrown!DateTimeException(Date(1999, 4, 30)); + assertNotThrown!DateTimeException(Date(1999, 5, 31)); + assertNotThrown!DateTimeException(Date(1999, 6, 30)); + assertNotThrown!DateTimeException(Date(1999, 7, 31)); + assertNotThrown!DateTimeException(Date(1999, 8, 31)); + assertNotThrown!DateTimeException(Date(1999, 9, 30)); + assertNotThrown!DateTimeException(Date(1999, 10, 31)); + assertNotThrown!DateTimeException(Date(1999, 11, 30)); + assertNotThrown!DateTimeException(Date(1999, 12, 31)); + + //Test B.C. + assertNotThrown!DateTimeException(Date(0, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 12, 31)); + assertNotThrown!DateTimeException(Date(-1, 2, 28)); + assertNotThrown!DateTimeException(Date(-4, 2, 29)); + + assertThrown!DateTimeException(Date(-1, 2, 29)); + assertThrown!DateTimeException(Date(-2, 2, 29)); + assertThrown!DateTimeException(Date(-3, 2, 29)); + } + + + /++ + Params: + day = The Xth day of the Gregorian Calendar that the constructed Date + will be for. + +/ + this(int day) pure nothrow + { + if(day > 0) + { + int years = (day / daysIn400Years) * 400 + 1; + day %= daysIn400Years; + + years += (day / daysIn100Years) * 100; + day %= daysIn100Years; + + years += (day / daysIn4Years) * 4; + day %= daysIn4Years; + + years += day / daysInYear; + day %= daysInYear; + + if(day == 0) + { + _year = cast(short)(years - 1); + _month = Month.dec; + _day = 31; + } + else + { + _year = cast(short)years; + + try + dayOfYear = day; + catch(Exception e) + assert(0, "dayOfYear assignment threw."); + } + } + else if(day <= 0 && -day < daysInLeapYear) + { + _year = 0; + + try + dayOfYear = (daysInLeapYear + day); + catch(Exception e) + assert(0, "dayOfYear assignment threw."); + } + else + { + day += daysInLeapYear - 1; + int years = (day / daysIn400Years) * 400 - 1; + day %= daysIn400Years; + + years += (day / daysIn100Years) * 100; + day %= daysIn100Years; + + years += (day / daysIn4Years) * 4; + day %= daysIn4Years; + + years += day / daysInYear; + day %= daysInYear; + + if(day == 0) + { + _year = cast(short)(years + 1); + _month = Month.jan; + _day = 1; + } + else + { + _year = cast(short)years; + immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1; + + try + dayOfYear = newDoY; + catch(Exception e) + assert(0, "dayOfYear assignment threw."); + } + } + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(1), Date(1, 1, 1)); + assertPred!"=="(Date(2), Date(1, 1, 2)); + assertPred!"=="(Date(32), Date(1, 2, 1)); + assertPred!"=="(Date(366), Date(2, 1, 1)); + assertPred!"=="(Date(731), Date(3, 1, 1)); + assertPred!"=="(Date(1096), Date(4, 1, 1)); + assertPred!"=="(Date(1462), Date(5, 1, 1)); + assertPred!"=="(Date(17_898), Date(50, 1, 1)); + assertPred!"=="(Date(35_065), Date(97, 1, 1)); + assertPred!"=="(Date(36_160), Date(100, 1, 1)); + assertPred!"=="(Date(36_525), Date(101, 1, 1)); + assertPred!"=="(Date(37_986), Date(105, 1, 1)); + assertPred!"=="(Date(72_684), Date(200, 1, 1)); + assertPred!"=="(Date(73_049), Date(201, 1, 1)); + assertPred!"=="(Date(109_208), Date(300, 1, 1)); + assertPred!"=="(Date(109_573), Date(301, 1, 1)); + assertPred!"=="(Date(145_732), Date(400, 1, 1)); + assertPred!"=="(Date(146_098), Date(401, 1, 1)); + assertPred!"=="(Date(182_257), Date(500, 1, 1)); + assertPred!"=="(Date(182_622), Date(501, 1, 1)); + assertPred!"=="(Date(364_878), Date(1000, 1, 1)); + assertPred!"=="(Date(365_243), Date(1001, 1, 1)); + assertPred!"=="(Date(584_023), Date(1600, 1, 1)); + assertPred!"=="(Date(584_389), Date(1601, 1, 1)); + assertPred!"=="(Date(693_596), Date(1900, 1, 1)); + assertPred!"=="(Date(693_961), Date(1901, 1, 1)); + assertPred!"=="(Date(729_755), Date(1999, 1, 1)); + assertPred!"=="(Date(730_120), Date(2000, 1, 1)); + assertPred!"=="(Date(730_486), Date(2001, 1, 1)); + + assertPred!"=="(Date(733_773), Date(2010, 1, 1)); + assertPred!"=="(Date(733_803), Date(2010, 1, 31)); + assertPred!"=="(Date(733_804), Date(2010, 2, 1)); + assertPred!"=="(Date(733_831), Date(2010, 2, 28)); + assertPred!"=="(Date(733_832), Date(2010, 3, 1)); + assertPred!"=="(Date(733_862), Date(2010, 3, 31)); + assertPred!"=="(Date(733_863), Date(2010, 4, 1)); + assertPred!"=="(Date(733_892), Date(2010, 4, 30)); + assertPred!"=="(Date(733_893), Date(2010, 5, 1)); + assertPred!"=="(Date(733_923), Date(2010, 5, 31)); + assertPred!"=="(Date(733_924), Date(2010, 6, 1)); + assertPred!"=="(Date(733_953), Date(2010, 6, 30)); + assertPred!"=="(Date(733_954), Date(2010, 7, 1)); + assertPred!"=="(Date(733_984), Date(2010, 7, 31)); + assertPred!"=="(Date(733_985), Date(2010, 8, 1)); + assertPred!"=="(Date(734_015), Date(2010, 8, 31)); + assertPred!"=="(Date(734_016), Date(2010, 9, 1)); + assertPred!"=="(Date(734_045), Date(2010, 9, 30)); + assertPred!"=="(Date(734_046), Date(2010, 10, 1)); + assertPred!"=="(Date(734_076), Date(2010, 10, 31)); + assertPred!"=="(Date(734_077), Date(2010, 11, 1)); + assertPred!"=="(Date(734_106), Date(2010, 11, 30)); + assertPred!"=="(Date(734_107), Date(2010, 12, 1)); + assertPred!"=="(Date(734_137), Date(2010, 12, 31)); + + assertPred!"=="(Date(734_534), Date(2012, 2, 1)); + assertPred!"=="(Date(734_561), Date(2012, 2, 28)); + assertPred!"=="(Date(734_562), Date(2012, 2, 29)); + assertPred!"=="(Date(734_563), Date(2012, 3, 1)); + + assertPred!"=="(Date(734_534), Date(2012, 2, 1)); + + assertPred!"=="(Date(734_561), Date(2012, 2, 28)); + assertPred!"=="(Date(734_562), Date(2012, 2, 29)); + assertPred!"=="(Date(734_563), Date(2012, 3, 1)); + + //Test B.C. + assertPred!"=="(Date(0), Date(0, 12, 31)); + assertPred!"=="(Date(-1), Date(0, 12, 30)); + assertPred!"=="(Date(-30), Date(0, 12, 1)); + assertPred!"=="(Date(-31), Date(0, 11, 30)); + + assertPred!"=="(Date(-366), Date(-1, 12, 31)); + assertPred!"=="(Date(-367), Date(-1, 12, 30)); + assertPred!"=="(Date(-730), Date(-1, 1, 1)); + assertPred!"=="(Date(-731), Date(-2, 12, 31)); + assertPred!"=="(Date(-1095), Date(-2, 1, 1)); + assertPred!"=="(Date(-1096), Date(-3, 12, 31)); + assertPred!"=="(Date(-1460), Date(-3, 1, 1)); + assertPred!"=="(Date(-1461), Date(-4, 12, 31)); + assertPred!"=="(Date(-1826), Date(-4, 1, 1)); + assertPred!"=="(Date(-1827), Date(-5, 12, 31)); + assertPred!"=="(Date(-2191), Date(-5, 1, 1)); + assertPred!"=="(Date(-3652), Date(-9, 1, 1)); + + assertPred!"=="(Date(-18_262), Date(-49, 1, 1)); + assertPred!"=="(Date(-18_627), Date(-50, 1, 1)); + assertPred!"=="(Date(-35_794), Date(-97, 1, 1)); + assertPred!"=="(Date(-36_160), Date(-99, 12, 31)); + assertPred!"=="(Date(-36_524), Date(-99, 1, 1)); + assertPred!"=="(Date(-36_889), Date(-100, 1, 1)); + assertPred!"=="(Date(-37_254), Date(-101, 1, 1)); + assertPred!"=="(Date(-38_715), Date(-105, 1, 1)); + assertPred!"=="(Date(-73_413), Date(-200, 1, 1)); + assertPred!"=="(Date(-73_778), Date(-201, 1, 1)); + assertPred!"=="(Date(-109_937), Date(-300, 1, 1)); + assertPred!"=="(Date(-110_302), Date(-301, 1, 1)); + assertPred!"=="(Date(-146_097), Date(-400, 12, 31)); + assertPred!"=="(Date(-146_462), Date(-400, 1, 1)); + assertPred!"=="(Date(-146_827), Date(-401, 1, 1)); + assertPred!"=="(Date(-182_621), Date(-499, 1, 1)); + assertPred!"=="(Date(-182_986), Date(-500, 1, 1)); + assertPred!"=="(Date(-183_351), Date(-501, 1, 1)); + assertPred!"=="(Date(-365_607), Date(-1000, 1, 1)); + assertPred!"=="(Date(-365_972), Date(-1001, 1, 1)); + assertPred!"=="(Date(-584_387), Date(-1599, 1, 1)); + assertPred!"=="(Date(-584_388), Date(-1600, 12, 31)); + assertPred!"=="(Date(-584_753), Date(-1600, 1, 1)); + assertPred!"=="(Date(-585_118), Date(-1601, 1, 1)); + assertPred!"=="(Date(-694_325), Date(-1900, 1, 1)); + assertPred!"=="(Date(-694_690), Date(-1901, 1, 1)); + assertPred!"=="(Date(-730_484), Date(-1999, 1, 1)); + assertPred!"=="(Date(-730_485), Date(-2000, 12, 31)); + assertPred!"=="(Date(-730_850), Date(-2000, 1, 1)); + assertPred!"=="(Date(-731_215), Date(-2001, 1, 1)); + + assertPred!"=="(Date(-734_502), Date(-2010, 1, 1)); + assertPred!"=="(Date(-734_472), Date(-2010, 1, 31)); + assertPred!"=="(Date(-734_471), Date(-2010, 2, 1)); + assertPred!"=="(Date(-734_444), Date(-2010, 2, 28)); + assertPred!"=="(Date(-734_443), Date(-2010, 3, 1)); + assertPred!"=="(Date(-734_413), Date(-2010, 3, 31)); + assertPred!"=="(Date(-734_412), Date(-2010, 4, 1)); + assertPred!"=="(Date(-734_383), Date(-2010, 4, 30)); + assertPred!"=="(Date(-734_382), Date(-2010, 5, 1)); + assertPred!"=="(Date(-734_352), Date(-2010, 5, 31)); + assertPred!"=="(Date(-734_351), Date(-2010, 6, 1)); + assertPred!"=="(Date(-734_322), Date(-2010, 6, 30)); + assertPred!"=="(Date(-734_321), Date(-2010, 7, 1)); + assertPred!"=="(Date(-734_291), Date(-2010, 7, 31)); + assertPred!"=="(Date(-734_290), Date(-2010, 8, 1)); + assertPred!"=="(Date(-734_260), Date(-2010, 8, 31)); + assertPred!"=="(Date(-734_259), Date(-2010, 9, 1)); + assertPred!"=="(Date(-734_230), Date(-2010, 9, 30)); + assertPred!"=="(Date(-734_229), Date(-2010, 10, 1)); + assertPred!"=="(Date(-734_199), Date(-2010, 10, 31)); + assertPred!"=="(Date(-734_198), Date(-2010, 11, 1)); + assertPred!"=="(Date(-734_169), Date(-2010, 11, 30)); + assertPred!"=="(Date(-734_168), Date(-2010, 12, 1)); + assertPred!"=="(Date(-734_138), Date(-2010, 12, 31)); + + assertPred!"=="(Date(-735_202), Date(-2012, 2, 1)); + assertPred!"=="(Date(-735_175), Date(-2012, 2, 28)); + assertPred!"=="(Date(-735_174), Date(-2012, 2, 29)); + assertPred!"=="(Date(-735_173), Date(-2012, 3, 1)); + + assertPred!"=="(Date(-1_373_427), Date(-3760, 9, 7)); //Start of the Hebrew Calendar + } + + + /++ + Compares this Date with the given Date. + + Returns: + $(TABLE + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in Date rhs) const pure nothrow + { + if(_year < rhs._year) + return -1; + if(_year > rhs._year) + return 1; + + if(_month < rhs._month) + return -1; + if(_month > rhs._month) + return 1; + + if(_day < rhs._day) + return -1; + if(_day > rhs._day) + return 1; + + return 0; + } + + unittest + { + //Test A.D. + assertPred!("opCmp", "==")(Date(1, 1, 1), Date.init); + + assertPred!("opCmp", "==")(Date(1999, 1, 1), Date(1999, 1, 1)); + assertPred!("opCmp", "==")(Date(1, 7, 1), Date(1, 7, 1)); + assertPred!("opCmp", "==")(Date(1, 1, 6), Date(1, 1, 6)); + + assertPred!("opCmp", "==")(Date(1999, 7, 1), Date(1999, 7, 1)); + assertPred!("opCmp", "==")(Date(1999, 7, 6), Date(1999, 7, 6)); + + assertPred!("opCmp", "==")(Date(1, 7, 6), Date(1, 7, 6)); + + assertPred!("opCmp", "<")(Date(1999, 7, 6), Date(2000, 7, 6)); + assertPred!("opCmp", ">")(Date(2000, 7, 6), Date(1999, 7, 6)); + assertPred!("opCmp", "<")(Date(1999, 7, 6), Date(1999, 8, 6)); + assertPred!("opCmp", ">")(Date(1999, 8, 6), Date(1999, 7, 6)); + assertPred!("opCmp", "<")(Date(1999, 7, 6), Date(1999, 7, 7)); + assertPred!("opCmp", ">")(Date(1999, 7, 7), Date(1999, 7, 6)); + + assertPred!("opCmp", "<")(Date(1999, 8, 7), Date(2000, 7, 6)); + assertPred!("opCmp", ">")(Date(2000, 8, 6), Date(1999, 7, 7)); + assertPred!("opCmp", "<")(Date(1999, 7, 7), Date(2000, 7, 6)); + assertPred!("opCmp", ">")(Date(2000, 7, 6), Date(1999, 7, 7)); + assertPred!("opCmp", "<")(Date(1999, 7, 7), Date(1999, 8, 6)); + assertPred!("opCmp", ">")(Date(1999, 8, 6), Date(1999, 7, 7)); + + //Test B.C. + assertPred!("opCmp", "==")(Date(0, 1, 1), Date(0, 1, 1)); + assertPred!("opCmp", "==")(Date(-1, 1, 1), Date(-1, 1, 1)); + assertPred!("opCmp", "==")(Date(-1, 7, 1), Date(-1, 7, 1)); + assertPred!("opCmp", "==")(Date(-1, 1, 6), Date(-1, 1, 6)); + + assertPred!("opCmp", "==")(Date(-1999, 7, 1), Date(-1999, 7, 1)); + assertPred!("opCmp", "==")(Date(-1999, 7, 6), Date(-1999, 7, 6)); + + assertPred!("opCmp", "==")(Date(-1, 7, 6), Date(-1, 7, 6)); + + assertPred!("opCmp", "<")(Date(-2000, 7, 6), Date(-1999, 7, 6)); + assertPred!("opCmp", ">")(Date(-1999, 7, 6), Date(-2000, 7, 6)); + assertPred!("opCmp", "<")(Date(-1999, 7, 6), Date(-1999, 8, 6)); + assertPred!("opCmp", ">")(Date(-1999, 8, 6), Date(-1999, 7, 6)); + assertPred!("opCmp", "<")(Date(-1999, 7, 6), Date(-1999, 7, 7)); + assertPred!("opCmp", ">")(Date(-1999, 7, 7), Date(-1999, 7, 6)); + + assertPred!("opCmp", "<")(Date(-2000, 8, 6), Date(-1999, 7, 7)); + assertPred!("opCmp", ">")(Date(-1999, 8, 7), Date(-2000, 7, 6)); + assertPred!("opCmp", "<")(Date(-2000, 7, 6), Date(-1999, 7, 7)); + assertPred!("opCmp", ">")(Date(-1999, 7, 7), Date(-2000, 7, 6)); + assertPred!("opCmp", "<")(Date(-1999, 7, 7), Date(-1999, 8, 6)); + assertPred!("opCmp", ">")(Date(-1999, 8, 6), Date(-1999, 7, 7)); + + //Test Both + assertPred!("opCmp", "<")(Date(-1999, 7, 6), Date(1999, 7, 6)); + assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 7, 6)); + + assertPred!("opCmp", "<")(Date(-1999, 8, 6), Date(1999, 7, 6)); + assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 8, 6)); + + assertPred!("opCmp", "<")(Date(-1999, 7, 7), Date(1999, 7, 6)); + assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 7, 7)); + + assertPred!("opCmp", "<")(Date(-1999, 8, 7), Date(1999, 7, 6)); + assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 8, 7)); + + assertPred!("opCmp", "<")(Date(-1999, 8, 6), Date(1999, 6, 6)); + assertPred!("opCmp", ">")(Date(1999, 6, 8), Date(-1999, 7, 6)); + + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.opCmp(date))); + static assert(__traits(compiles, date.opCmp(cdate))); + static assert(__traits(compiles, date.opCmp(idate))); + static assert(__traits(compiles, cdate.opCmp(date))); + static assert(__traits(compiles, cdate.opCmp(cdate))); + static assert(__traits(compiles, cdate.opCmp(idate))); + static assert(__traits(compiles, idate.opCmp(date))); + static assert(__traits(compiles, idate.opCmp(cdate))); + static assert(__traits(compiles, idate.opCmp(idate))); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Examples: +-------------------- +assert(Date(1999, 7, 6).year == 1999); +assert(Date(2010, 10, 4).year == 2010); +assert(Date(-7, 4, 5).year == -7); +-------------------- + +/ + @property short year() const pure nothrow + { + return _year; + } + + unittest + { + assertPred!"=="(Date.init.year, 1); + assertPred!"=="(Date(1999, 7, 6).year, 1999); + assertPred!"=="(Date(-1999, 7, 6).year, -1999); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.year == 1999)); + static assert(__traits(compiles, idate.year == 1999)); + + //Verify Examples. + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this Date's year to. + + Throws: + DateTimeException if the new year is not a leap year and the resulting + date would be on February 29th. + +/ + @property void year(int year) pure + { + enforceValid!"days"(year, _month, _day); + _year = cast(short)year; + } + + unittest + { + static void testDateInvalid(Date date, int year) + { + date.year = year; + } + + static void testDate(Date date, int year, in Date expected, size_t line = __LINE__) + { + date.year = year; + assertPred!"=="(date, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); + + testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); + testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); + testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.year = 1999)); + static assert(!__traits(compiles, idate.year = 1999)); + + //Verify Examples. + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + DateTimeException if $(D isAD) is true. + + Examples: +-------------------- +assert(Date(0, 1, 1).yearBC == 1); +assert(Date(-1, 1, 1).yearBC == 2); +assert(Date(-100, 1, 1).yearBC == 101); +-------------------- + +/ + @property ushort yearBC() const pure + { + if(isAD) + throw new DateTimeException("Year " ~ numToString(_year) ~ " is A.D."); + //Once format is pure, this would be a better error message. + //throw new DateTimeException(format("%s is A.D.", this)); + + return cast(ushort)((_year * -1) + 1); + } + + unittest + { + assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); + + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + static assert(__traits(compiles, date.yearBC)); + static assert(__traits(compiles, cdate.yearBC)); + static assert(__traits(compiles, idate.yearBC)); + + //Verify Examples. + assert(Date(0, 1, 1).yearBC == 1); + assert(Date(-1, 1, 1).yearBC == 2); + assert(Date(-100, 1, 1).yearBC == 101); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this Date's year to. + + Throws: + DateTimeException if a non-positive value is given. + + Examples: +-------------------- +auto date = Date(2010, 1, 1); +date.yearBC = 1; +assert(date == Date(0, 1, 1)); + +date.yearBC = 10; +assert(date == Date(-9, 1, 1)); +-------------------- + +/ + @property void yearBC(int year) pure + { + if(year <= 0) + throw new DateTimeException("The given year is not a year B.C."); + + _year = cast(short)((year - 1) * -1); + } + + unittest + { + assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); + + { + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + static assert(__traits(compiles, date.yearBC = 7)); + static assert(!__traits(compiles, cdate.yearBC = 7)); + static assert(!__traits(compiles, idate.yearBC = 7)); + } + + //Verify Examples. + { + auto date = Date(2010, 1, 1); + date.yearBC = 1; + assert(date == Date(0, 1, 1)); + + date.yearBC = 10; + assert(date == Date(-9, 1, 1)); + } + } + + + /++ + Month of a Gregorian Year. + + Examples: +-------------------- +assert(Date(1999, 7, 6).month == 7); +assert(Date(2010, 10, 4).month == 10); +assert(Date(-7, 4, 5).month == 4); +-------------------- + +/ + @property Month month() const pure nothrow + { + return _month; + } + + unittest + { + assertPred!"=="(Date.init.month, 1); + assertPred!"=="(Date(1999, 7, 6).month, 7); + assertPred!"=="(Date(-1999, 7, 6).month, 7); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.month == 7)); + static assert(__traits(compiles, idate.month == 7)); + + //Verify Examples. + assert(Date(1999, 7, 6).month == 7); + assert(Date(2010, 10, 4).month == 10); + assert(Date(-7, 4, 5).month == 4); + } + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this Date's month to. + + Throws: + DateTimeException if the given month is not a valid month or if + the current day would not be valid in the given month. + +/ + @property void month(Month month) pure + { + enforceValid!"months"(month); + enforceValid!"days"(_year, month, _day); + _month = cast(Month)month; + } + + unittest + { + static void testDate(Date date, Month month, in Date expected = Date.init, size_t line = __LINE__) + { + date.month = month; + assert(expected != Date.init); + assertPred!"=="(date, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)13)); + assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month)2)); + assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month)2)); + + testDate(Date(1, 1, 1), cast(Month)7, Date(1, 7, 1)); + testDate(Date(-1, 1, 1), cast(Month)7, Date(-1, 7, 1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.month = 7)); + static assert(!__traits(compiles, idate.month = 7)); + } + + + /++ + Day of a Gregorian Month. + + Examples: +-------------------- +assert(Date(1999, 7, 6).day == 6); +assert(Date(2010, 10, 4).day == 4); +assert(Date(-7, 4, 5).day == 5); +-------------------- + +/ + @property ubyte day() const pure nothrow + { + return _day; + } + + unittest + { + assertPred!"=="(Date.init.day, 1); + assertPred!"=="(Date(1999, 7, 6).day, 6); + assertPred!"=="(Date(-1999, 7, 6).day, 6); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.day == 6)); + static assert(__traits(compiles, idate.day == 6)); + + //Verify Examples. + assert(Date(1999, 7, 6).day == 6); + assert(Date(2010, 10, 4).day == 4); + assert(Date(-7, 4, 5).day == 5); + } + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this Date's day to. + + Throws: + DateTimeException if the given day is not a valid day of the current month. + +/ + @property void day(int day) pure + { + enforceValid!"days"(_year, _month, day); + _day = cast(ubyte)day; + } + + unittest + { + static void testDate(Date date, int day) + { + date.day = day; + } + + //Test A.D. + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); + + { + auto date = Date(1, 1, 1); + date.day = 6; + assertPred!"=="(date, Date(1, 1, 6)); + } + + //Test B.C. + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); + + { + auto date = Date(-1, 1, 1); + date.day = 6; + assertPred!"=="(date, Date(-1, 1, 6)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.day = 6)); + static assert(!__traits(compiles, idate.day = 6)); + } + + + /++ + Adds the given number of years to this Date. A negative number will subtract. + + Note that if day overflow is allowed, and the date is Febuary 29th of a + leap year, and the new year is not a leap year, then the date is shifted + to March 1st. If day overflow is not allowed, then the date is shifted to + February 28th. + + Params: + years = The number of years to add to this Date. + allowOverflow = Whether the days should be allowed to overflow, causing + the month to increment. + + Examples: +-------------------- +auto date1 = Date(2010, 1, 1); +date1.add!"years"(1); +assert(date1 == Date(2011, 1, 1)); + +auto date2 = Date(2010, 1, 1); +date2.add!"years"(-1); +assert(date2 == Date(2009, 1, 1)); + +auto date3 = Date(2000, 2, 29); +date3.add!"years"(1); +assert(date3 == Date(2001, 3, 1)); + +auto date4 = Date(2000, 2, 29); +date4.add!"years"(1, AllowDayOverflow.no); +assert(date4 == Date(2001, 2, 28)); +-------------------- + +/ + /+ref Date+/ void add(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + if(units == "years") + { + immutable newYear = _year + years; + + _year += years; + + if(_month == Month.feb && _day == 29 && !yearIsLeapYear(_year)) + { + if(allowOverflow == AllowDayOverflow.yes) + { + _month = Month.mar; + _day = 1; + } + else + _day = 28; + } + } + + //Test add!"years"() with AllowDayOverlow.yes + unittest + { + //Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"years"(7); + assertPred!"=="(date, Date(2006, 7, 6)); + date.add!"years"(-9); + assertPred!"=="(date, Date(1997, 7, 6)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"years"(1); + assertPred!"=="(date, Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1); + assertPred!"=="(date, Date(1999, 3, 1)); + } + + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7); + assertPred!"=="(date, Date(-2006, 7, 6)); + date.add!"years"(9); + assertPred!"=="(date, Date(-1997, 7, 6)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1); + assertPred!"=="(date, Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1); + assertPred!"=="(date, Date(-1999, 3, 1)); + } + + //Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5); + assertPred!"=="(date, Date(-1, 7, 6)); + date.add!"years"(5); + assertPred!"=="(date, Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(5); + assertPred!"=="(date, Date(1, 7, 6)); + date.add!"years"(-5); + assertPred!"=="(date, Date(-4, 7, 6)); + } + + { + auto date = Date(4, 7, 6); + date.add!"years"(-8); + assertPred!"=="(date, Date(-4, 7, 6)); + date.add!"years"(8); + assertPred!"=="(date, Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(8); + assertPred!"=="(date, Date(4, 7, 6)); + date.add!"years"(-8); + assertPred!"=="(date, Date(-4, 7, 6)); + } + + { + auto date = Date(-4, 2, 29); + date.add!"years"(5); + assertPred!"=="(date, Date(1, 3, 1)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5); + assertPred!"=="(date, Date(-1, 3, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"years"(7))); + static assert(!__traits(compiles, idate.add!"years"(7))); + + //Verify Examples. + auto date1 = Date(2010, 1, 1); + date1.add!"years"(1); + assert(date1 == Date(2011, 1, 1)); + + auto date2 = Date(2010, 1, 1); + date2.add!"years"(-1); + assert(date2 == Date(2009, 1, 1)); + + auto date3 = Date(2000, 2, 29); + date3.add!"years"(1); + assert(date3 == Date(2001, 3, 1)); + + auto date4 = Date(2000, 2, 29); + date4.add!"years"(1, AllowDayOverflow.no); + assert(date4 == Date(2001, 2, 28)); + } + + //Test add!"years"() with AllowDayOverlow.no + unittest + { + //Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"years"(7, AllowDayOverflow.no); + assertPred!"=="(date, Date(2006, 7, 6)); + date.add!"years"(-9, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 7, 6)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 2, 28)); + } + + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2006, 7, 6)); + date.add!"years"(9, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 7, 6)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 2, 28)); + } + + //Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1, 7, 6)); + date.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(date, Date(1, 7, 6)); + date.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 7, 6)); + } + + { + auto date = Date(4, 7, 6); + date.add!"years"(-8, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 7, 6)); + date.add!"years"(8, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(8, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 7, 6)); + date.add!"years"(-8, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 7, 6)); + } + + { + auto date = Date(-4, 2, 29); + date.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(date, Date(1, 2, 28)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1, 2, 28)); + } + } + + + /++ + Adds the given number of months to this Date. A negative number will subtract. + + The year will be adjusted along with the month if the number of months added + (or subtracted) would overflow (or underflow) the current year. + + Note that if day overflow is allowed, and the date with the adjusted month + overflows the number of days in the new month, then the month will be + incremented by one, and the days 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: + months = The number of months to add to this Date. + allowOverflow = Whether the days should be allowed to overflow, causing + the month to increment. + + Examples: +-------------------- +auto date1 = Date(2010, 1, 1); +date1.add!"months"(1); +assert(date1 == Date(2010, 2, 1)); + +auto date2 = Date(2010, 1, 1); +date2.add!"months"(-1); +assert(date2 == Date(2009, 12, 1)); + +auto date3 = Date(1999, 1, 29); +date3.add!"months"(1); +assert(date3 == Date(1999, 3, 1)); + +auto date4 = Date(1999, 1, 29); +date4.add!"months"(1, AllowDayOverflow.no); +assert(date4 == Date(1999, 2, 28)); +-------------------- + +/ + /+ref Date+/ void add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + if(units == "months") + { + auto years = months / 12; + months %= 12; + auto newMonth = _month + months; + + if(months < 0) + { + if(newMonth < 1) + { + newMonth += 12; + --years; + } + } + else if(newMonth > 12) + { + newMonth -= 12; + ++years; + } + + _year += years; + _month = cast(Month)newMonth; + + immutable currMaxDay = maxDay(_year, _month); + immutable overflow = _day - currMaxDay; + + if(overflow > 0) + { + if(allowOverflow == AllowDayOverflow.yes) + { + ++_month; + _day = cast(ubyte)overflow; + } + else + _day = cast(ubyte)currMaxDay; + } + } + + //Test add!"months"() with AllowDayOverlow.yes + unittest + { + //Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"months"(3); + assertPred!"=="(date, Date(1999, 10, 6)); + date.add!"months"(-4); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(6); + assertPred!"=="(date, Date(2000, 1, 6)); + date.add!"months"(-6); + assertPred!"=="(date, Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(27); + assertPred!"=="(date, Date(2001, 10, 6)); + date.add!"months"(-28); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(1); + assertPred!"=="(date, Date(1999, 7, 1)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1); + assertPred!"=="(date, Date(1999, 5, 1)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"months"(12); + assertPred!"=="(date, Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"months"(12); + assertPred!"=="(date, Date(2001, 3, 1)); + } + + { + auto date = Date(1999, 7, 31); + date.add!"months"(1); + assertPred!"=="(date, Date(1999, 8, 31)); + date.add!"months"(1); + assertPred!"=="(date, Date(1999, 10, 1)); + } + + { + auto date = Date(1998, 8, 31); + date.add!"months"(13); + assertPred!"=="(date, Date(1999, 10, 1)); + date.add!"months"(-13); + assertPred!"=="(date, Date(1998, 9, 1)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(13); + assertPred!"=="(date, Date(1999, 1, 31)); + date.add!"months"(-13); + assertPred!"=="(date, Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(14); + assertPred!"=="(date, Date(1999, 3, 3)); + date.add!"months"(-14); + assertPred!"=="(date, Date(1998, 1, 3)); + } + + { + auto date = Date(1998, 12, 31); + date.add!"months"(14); + assertPred!"=="(date, Date(2000, 3, 2)); + date.add!"months"(-14); + assertPred!"=="(date, Date(1999, 1, 2)); + } + + { + auto date = Date(1999, 12, 31); + date.add!"months"(14); + assertPred!"=="(date, Date(2001, 3, 3)); + date.add!"months"(-14); + assertPred!"=="(date, Date(2000, 1, 3)); + } + + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3); + assertPred!"=="(date, Date(-1999, 10, 6)); + date.add!"months"(-4); + assertPred!"=="(date, Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6); + assertPred!"=="(date, Date(-1998, 1, 6)); + date.add!"months"(-6); + assertPred!"=="(date, Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27); + assertPred!"=="(date, Date(-2001, 4, 6)); + date.add!"months"(28); + assertPred!"=="(date, Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1); + assertPred!"=="(date, Date(-1999, 7, 1)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1); + assertPred!"=="(date, Date(-1999, 5, 1)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12); + assertPred!"=="(date, Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12); + assertPred!"=="(date, Date(-2001, 3, 1)); + } + + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1); + assertPred!"=="(date, Date(-1999, 8, 31)); + date.add!"months"(1); + assertPred!"=="(date, Date(-1999, 10, 1)); + } + + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13); + assertPred!"=="(date, Date(-1997, 10, 1)); + date.add!"months"(-13); + assertPred!"=="(date, Date(-1998, 9, 1)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13); + assertPred!"=="(date, Date(-1995, 1, 31)); + date.add!"months"(-13); + assertPred!"=="(date, Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14); + assertPred!"=="(date, Date(-1995, 3, 3)); + date.add!"months"(-14); + assertPred!"=="(date, Date(-1996, 1, 3)); + } + + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14); + assertPred!"=="(date, Date(-2000, 3, 2)); + date.add!"months"(-14); + assertPred!"=="(date, Date(-2001, 1, 2)); + } + + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14); + assertPred!"=="(date, Date(-1999, 3, 3)); + date.add!"months"(-14); + assertPred!"=="(date, Date(-2000, 1, 3)); + } + + //Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1); + assertPred!"=="(date, Date(0, 12, 1)); + date.add!"months"(1); + assertPred!"=="(date, Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.add!"months"(-48); + assertPred!"=="(date, Date(0, 1, 1)); + date.add!"months"(48); + assertPred!"=="(date, Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-49); + assertPred!"=="(date, Date(0, 3, 2)); + date.add!"months"(49); + assertPred!"=="(date, Date(4, 4, 2)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-85); + assertPred!"=="(date, Date(-3, 3, 3)); + date.add!"months"(85); + assertPred!"=="(date, Date(4, 4, 3)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"months"(3))); + static assert(!__traits(compiles, idate.add!"months"(3))); + + //Verify Examples. + auto date1 = Date(2010, 1, 1); + date1.add!"months"(1); + assert(date1 == Date(2010, 2, 1)); + + auto date2 = Date(2010, 1, 1); + date2.add!"months"(-1); + assert(date2 == Date(2009, 12, 1)); + + auto date3 = Date(1999, 1, 29); + date3.add!"months"(1); + assert(date3 == Date(1999, 3, 1)); + + auto date4 = Date(1999, 1, 29); + date4.add!"months"(1, AllowDayOverflow.no); + assert(date4 == Date(1999, 2, 28)); + } + + //Test add!"months"() with AllowDayOverlow.no + unittest + { + //Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assertPred!"=="(date, Date(2000, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(27, AllowDayOverflow.no); + assertPred!"=="(date, Date(2001, 10, 6)); + date.add!"months"(-28, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 4, 30)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"months"(12, AllowDayOverflow.no); + assertPred!"=="(date, Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"months"(12, AllowDayOverflow.no); + assertPred!"=="(date, Date(2001, 2, 28)); + } + + { + auto date = Date(1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 9, 30)); + } + + { + auto date = Date(1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1998, 8, 30)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 12, 28)); + } + + { + auto date = Date(1998, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1998, 12, 29)); + } + + { + auto date = Date(1999, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(2001, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 12, 28)); + } + + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1998, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2001, 4, 6)); + date.add!"months"(28, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 4, 30)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2001, 2, 28)); + } + + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 9, 30)); + } + + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1998, 8, 30)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1995, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1995, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 12, 28)); + } + + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2002, 12, 29)); + } + + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2001, 12, 28)); + } + + //Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(0, 12, 1)); + date.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.add!"months"(-48, AllowDayOverflow.no); + assertPred!"=="(date, Date(0, 1, 1)); + date.add!"months"(48, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-49, AllowDayOverflow.no); + assertPred!"=="(date, Date(0, 2, 29)); + date.add!"months"(49, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 3, 29)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(date, Date(-3, 2, 28)); + date.add!"months"(85, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 3, 28)); + } + } + + + /++ + Adds the given number of years to this Date. A negative number will subtract. + + For years, because they are the largest unit in Date, there is no difference + between adding or rolling. + + Params: + years = The number of years to add to this Date. + allowOverflow = Whether the days should be allowed to overflow, causing + the month to increment. + +/ + /+ref Date+/ void roll(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + if(units == "years") + { + add!"years"(years, allowOverflow); + } + + unittest + { + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"years"(3))); + static assert(!__traits(compiles, idate.rolYears(3))); + } + + + /++ + Adds the given number of months to this Date. A negative number will subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the Date 12 months, you get the exact same + Date. However, the days can still be affected due to the differing number + of days in each month. + + Params: + months = The number of months to add to this Date. + allowOverflow = Whether the days should be allowed to overflow, causing + the month to increment. + + Examples: +-------------------- +auto date1 = Date(2010, 1, 1); +date1.roll!"months"(1); +assert(date1 == Date(2010, 2, 1)); + +auto date2 = Date(2010, 1, 1); +date2.roll!"months"(-1); +assert(date2 == Date(2010, 12, 1)); + +auto date3 = Date(1999, 1, 29); +date3.roll!"months"(1); +assert(date3 == Date(1999, 3, 1)); + +auto date4 = Date(1999, 1, 29); +date4.roll!"months"(1, AllowDayOverflow.no); +assert(date4 == Date(1999, 2, 28)); +-------------------- + +/ + /+ref Date+/ void roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + if(units == "months") + { + months %= 12; + auto newMonth = _month + months; + + if(months < 0) + { + if(newMonth < 1) + newMonth += 12; + } + else + { + if(newMonth > 12) + newMonth -= 12; + } + + _month = cast(Month)newMonth; + + immutable currMaxDay = maxDay(_year, _month); + immutable overflow = _day - currMaxDay; + + if(overflow > 0) + { + if(allowOverflow == AllowDayOverflow.yes) + { + ++_month; + _day = cast(ubyte)overflow; + } + else + _day = cast(ubyte)currMaxDay; + } + } + + //Test roll!"months"() with AllowDayOverlow.yes + unittest + { + //Test A.D. + { + auto date = Date(1999, 7, 6); + date.roll!"months"(3); + assertPred!"=="(date, Date(1999, 10, 6)); + date.roll!"months"(-4); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6); + assertPred!"=="(date, Date(1999, 1, 6)); + date.roll!"months"(-6); + assertPred!"=="(date, Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27); + assertPred!"=="(date, Date(1999, 10, 6)); + date.roll!"months"(-28); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1); + assertPred!"=="(date, Date(1999, 7, 1)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1); + assertPred!"=="(date, Date(1999, 5, 1)); + } + + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12); + assertPred!"=="(date, Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12); + assertPred!"=="(date, Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1); + assertPred!"=="(date, Date(1999, 8, 31)); + date.roll!"months"(1); + assertPred!"=="(date, Date(1999, 10, 1)); + } + + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13); + assertPred!"=="(date, Date(1998, 10, 1)); + date.roll!"months"(-13); + assertPred!"=="(date, Date(1998, 9, 1)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13); + assertPred!"=="(date, Date(1997, 1, 31)); + date.roll!"months"(-13); + assertPred!"=="(date, Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14); + assertPred!"=="(date, Date(1997, 3, 3)); + date.roll!"months"(-14); + assertPred!"=="(date, Date(1997, 1, 3)); + } + + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14); + assertPred!"=="(date, Date(1998, 3, 3)); + date.roll!"months"(-14); + assertPred!"=="(date, Date(1998, 1, 3)); + } + + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14); + assertPred!"=="(date, Date(1999, 3, 3)); + date.roll!"months"(-14); + assertPred!"=="(date, Date(1999, 1, 3)); + } + + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3); + assertPred!"=="(date, Date(-1999, 10, 6)); + date.roll!"months"(-4); + assertPred!"=="(date, Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6); + assertPred!"=="(date, Date(-1999, 1, 6)); + date.roll!"months"(-6); + assertPred!"=="(date, Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27); + assertPred!"=="(date, Date(-1999, 4, 6)); + date.roll!"months"(28); + assertPred!"=="(date, Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1); + assertPred!"=="(date, Date(-1999, 7, 1)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1); + assertPred!"=="(date, Date(-1999, 5, 1)); + } + + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12); + assertPred!"=="(date, Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12); + assertPred!"=="(date, Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1); + assertPred!"=="(date, Date(-1999, 8, 31)); + date.roll!"months"(1); + assertPred!"=="(date, Date(-1999, 10, 1)); + } + + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13); + assertPred!"=="(date, Date(-1998, 10, 1)); + date.roll!"months"(-13); + assertPred!"=="(date, Date(-1998, 9, 1)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13); + assertPred!"=="(date, Date(-1997, 1, 31)); + date.roll!"months"(-13); + assertPred!"=="(date, Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14); + assertPred!"=="(date, Date(-1997, 3, 3)); + date.roll!"months"(-14); + assertPred!"=="(date, Date(-1997, 1, 3)); + } + + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14); + assertPred!"=="(date, Date(-2002, 3, 3)); + date.roll!"months"(-14); + assertPred!"=="(date, Date(-2002, 1, 3)); + } + + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14); + assertPred!"=="(date, Date(-2001, 3, 3)); + date.roll!"months"(-14); + assertPred!"=="(date, Date(-2001, 1, 3)); + } + + //Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1); + assertPred!"=="(date, Date(1, 12, 1)); + date.roll!"months"(1); + assertPred!"=="(date, Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48); + assertPred!"=="(date, Date(4, 1, 1)); + date.roll!"months"(48); + assertPred!"=="(date, Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49); + assertPred!"=="(date, Date(4, 3, 2)); + date.roll!"months"(49); + assertPred!"=="(date, Date(4, 4, 2)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85); + assertPred!"=="(date, Date(4, 3, 2)); + date.roll!"months"(85); + assertPred!"=="(date, Date(4, 4, 2)); + } + + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1); + assertPred!"=="(date, Date(-1, 12, 1)); + date.roll!"months"(1); + assertPred!"=="(date, Date(-1, 1, 1)); + } + + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48); + assertPred!"=="(date, Date(-4, 1, 1)); + date.roll!"months"(48); + assertPred!"=="(date, Date(-4, 1, 1)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49); + assertPred!"=="(date, Date(-4, 3, 2)); + date.roll!"months"(49); + assertPred!"=="(date, Date(-4, 4, 2)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85); + assertPred!"=="(date, Date(-4, 3, 2)); + date.roll!"months"(85); + assertPred!"=="(date, Date(-4, 4, 2)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"months"(3))); + static assert(!__traits(compiles, idate.roll!"months"(3))); + + //Verify Examples. + auto date1 = Date(2010, 1, 1); + date1.roll!"months"(1); + assert(date1 == Date(2010, 2, 1)); + + auto date2 = Date(2010, 1, 1); + date2.roll!"months"(-1); + assert(date2 == Date(2010, 12, 1)); + + auto date3 = Date(1999, 1, 29); + date3.roll!"months"(1); + assert(date3 == Date(1999, 3, 1)); + + auto date4 = Date(1999, 1, 29); + date4.roll!"months"(1, AllowDayOverflow.no); + assert(date4 == Date(1999, 2, 28)); + } + + //Test roll!"months"() with AllowDayOverlow.no + unittest + { + //Test A.D. + { + auto date = Date(1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 10, 6)); + date.roll!"months"(-28, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 4, 30)); + } + + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12, AllowDayOverflow.no); + assertPred!"=="(date, Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 9, 30)); + } + + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1998, 8, 30)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1997, 12, 28)); + } + + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1998, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1998, 12, 28)); + } + + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(1999, 12, 28)); + } + + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 4, 6)); + date.roll!"months"(28, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 4, 30)); + } + + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1999, 9, 30)); + } + + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1998, 8, 30)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1997, 12, 28)); + } + + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2002, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2002, 12, 28)); + } + + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2001, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(date, Date(-2001, 12, 28)); + } + + //Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 3, 29)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assertPred!"=="(date, Date(4, 3, 29)); + } + + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(date, Date(-1, 1, 1)); + } + + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 1, 1)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 3, 29)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assertPred!"=="(date, Date(-4, 3, 29)); + } + } + + + /++ + Adds the given number of days to this Date. A negative number will subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the Date one year's worth of days, then you + get the exact same Date. + + Note that there is no $(D add!"days"()) because you can add days to a Date by + adding a duration to it. + + Params: + days = The number of days to add to this Date. + + Examples: +-------------------- +auto date = Date(2010, 1, 1); +date.roll!"days"(1); +assert(date == Date(2010, 1, 2)); +date.roll!"days"(365); +assert(date == Date(2010, 1, 26)); +date.roll!"days"(-32); +assert(date == Date(2010, 1, 25)); +-------------------- + +/ + /+ref Date+/ void roll(string units)(long days) pure nothrow + if(units == "days") + { + immutable limit = maxDay(_year, _month); + days %= limit; + auto newDay = _day + days; + + if(days < 0) + { + if(newDay < 1) + newDay += limit; + } + else if(newDay > limit) + newDay -= limit; + + _day = cast(ubyte)newDay; + } + + unittest + { + //Test A.D. + { + auto date = Date(1999, 2, 28); + date.roll!"days"(1); + assertPred!"=="(date, Date(1999, 2, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 28); + date.roll!"days"(1); + assertPred!"=="(date, Date(2000, 2, 29)); + date.roll!"days"(1); + assertPred!"=="(date, Date(2000, 2, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 6, 30); + date.roll!"days"(1); + assertPred!"=="(date, Date(1999, 6, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"days"(1); + assertPred!"=="(date, Date(1999, 7, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 1, 1); + date.roll!"days"(-1); + assertPred!"=="(date, Date(1999, 1, 31)); + date.roll!"days"(1); + assertPred!"=="(date, Date(1999, 1, 1)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"days"(9); + assertPred!"=="(date, Date(1999, 7, 15)); + date.roll!"days"(-11); + assertPred!"=="(date, Date(1999, 7, 4)); + date.roll!"days"(30); + assertPred!"=="(date, Date(1999, 7, 3)); + date.roll!"days"(-3); + assertPred!"=="(date, Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"days"(365); + assertPred!"=="(date, Date(1999, 7, 30)); + date.roll!"days"(-365); + assertPred!"=="(date, Date(1999, 7, 6)); + date.roll!"days"(366); + assertPred!"=="(date, Date(1999, 7, 31)); + date.roll!"days"(730); + assertPred!"=="(date, Date(1999, 7, 17)); + date.roll!"days"(-1096); + assertPred!"=="(date, Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 2, 6); + date.roll!"days"(365); + assertPred!"=="(date, Date(1999, 2, 7)); + date.roll!"days"(-365); + assertPred!"=="(date, Date(1999, 2, 6)); + date.roll!"days"(366); + assertPred!"=="(date, Date(1999, 2, 8)); + date.roll!"days"(730); + assertPred!"=="(date, Date(1999, 2, 10)); + date.roll!"days"(-1096); + assertPred!"=="(date, Date(1999, 2, 6)); + } + + //Test B.C. + { + auto date = Date(-1999, 2, 28); + date.roll!"days"(1); + assertPred!"=="(date, Date(-1999, 2, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 28); + date.roll!"days"(1); + assertPred!"=="(date, Date(-2000, 2, 29)); + date.roll!"days"(1); + assertPred!"=="(date, Date(-2000, 2, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 6, 30); + date.roll!"days"(1); + assertPred!"=="(date, Date(-1999, 6, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"days"(1); + assertPred!"=="(date, Date(-1999, 7, 1)); + date.roll!"days"(-1); + assertPred!"=="(date, Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 1, 1); + date.roll!"days"(-1); + assertPred!"=="(date, Date(-1999, 1, 31)); + date.roll!"days"(1); + assertPred!"=="(date, Date(-1999, 1, 1)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(9); + assertPred!"=="(date, Date(-1999, 7, 15)); + date.roll!"days"(-11); + assertPred!"=="(date, Date(-1999, 7, 4)); + date.roll!"days"(30); + assertPred!"=="(date, Date(-1999, 7, 3)); + date.roll!"days"(-3); + assertPred!"=="(date, Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(365); + assertPred!"=="(date, Date(-1999, 7, 30)); + date.roll!"days"(-365); + assertPred!"=="(date, Date(-1999, 7, 6)); + date.roll!"days"(366); + assertPred!"=="(date, Date(-1999, 7, 31)); + date.roll!"days"(730); + assertPred!"=="(date, Date(-1999, 7, 17)); + date.roll!"days"(-1096); + assertPred!"=="(date, Date(-1999, 7, 6)); + } + + //Test Both + { + auto date = Date(1, 7, 6); + date.roll!"days"(-365); + assertPred!"=="(date, Date(1, 7, 13)); + date.roll!"days"(365); + assertPred!"=="(date, Date(1, 7, 6)); + date.roll!"days"(-731); + assertPred!"=="(date, Date(1, 7, 19)); + date.roll!"days"(730); + assertPred!"=="(date, Date(1, 7, 5)); + } + + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365); + assertPred!"=="(date, Date(0, 7, 13)); + date.roll!"days"(365); + assertPred!"=="(date, Date(0, 7, 6)); + date.roll!"days"(-731); + assertPred!"=="(date, Date(0, 7, 19)); + date.roll!"days"(730); + assertPred!"=="(date, Date(0, 7, 5)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"days"(12))); + static assert(!__traits(compiles, idate.roll!"days"(12))); + + //Verify Examples. + auto date = Date(2010, 1, 1); + date.roll!"days"(1); + assert(date == Date(2010, 1, 2)); + date.roll!"days"(365); + assert(date == Date(2010, 1, 26)); + date.roll!"days"(-32); + assert(date == Date(2010, 1, 25)); + } + + + /++ + Gives the result of adding or subtracting a duration from this Date. + + The legal types of arithmetic for Date using this operator are + + $(TABLE + $(TR $(TD Date) $(TD +) $(TD duration) $(TD -->) $(TD Date)) + $(TR $(TD Date) $(TD -) $(TD duration) $(TD -->) $(TD Date)) + ) + + Params: + duration = The duration to add to or subtract from this Date. + +/ + Date opBinary(string op, D)(in D duration) const pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + Date retval = this; + + static if(is(Unqual!D == Duration)) + immutable days = duration.total!"days"; + else static if(is(Unqual!D == TickDuration)) + immutable days = convert!("hnsecs", "days")(duration.hnsecs); + + //Ideally, this would just be + //return retval.addDays(unaryFun!(op ~ "a")(days)); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedDays = days; + else static if(op == "-") + immutable signedDays = -days; + else + static assert(0); + + return retval.addDays(signedDays); + } + + unittest + { + auto date = Date(1999, 7, 6); + + assertPred!"=="(date + dur!"weeks"(7), Date(1999, 8, 24)); + assertPred!"=="(date + dur!"weeks"(-7), Date(1999, 5, 18)); + assertPred!"=="(date + dur!"days"(7), Date(1999, 7, 13)); + assertPred!"=="(date + dur!"days"(-7), Date(1999, 6, 29)); + + assertPred!"=="(date + dur!"hours"(24), Date(1999, 7, 7)); + assertPred!"=="(date + dur!"hours"(-24), Date(1999, 7, 5)); + assertPred!"=="(date + dur!"minutes"(1440), Date(1999, 7, 7)); + assertPred!"=="(date + dur!"minutes"(-1440), Date(1999, 7, 5)); + assertPred!"=="(date + dur!"seconds"(86_400), Date(1999, 7, 7)); + assertPred!"=="(date + dur!"seconds"(-86_400), Date(1999, 7, 5)); + assertPred!"=="(date + dur!"msecs"(86_400_000), Date(1999, 7, 7)); + assertPred!"=="(date + dur!"msecs"(-86_400_000), Date(1999, 7, 5)); + assertPred!"=="(date + dur!"usecs"(86_400_000_000), Date(1999, 7, 7)); + assertPred!"=="(date + dur!"usecs"(-86_400_000_000), Date(1999, 7, 5)); + assertPred!"=="(date + dur!"hnsecs"(864_000_000_000), Date(1999, 7, 7)); + assertPred!"=="(date + dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 5)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(date + TickDuration.from!"usecs"(86_400_000_000), Date(1999, 7, 7)); + assertPred!"=="(date + TickDuration.from!"usecs"(-86_400_000_000), Date(1999, 7, 5)); + } + + assertPred!"=="(date - dur!"weeks"(-7), Date(1999, 8, 24)); + assertPred!"=="(date - dur!"weeks"(7), Date(1999, 5, 18)); + assertPred!"=="(date - dur!"days"(-7), Date(1999, 7, 13)); + assertPred!"=="(date - dur!"days"(7), Date(1999, 6, 29)); + + assertPred!"=="(date - dur!"hours"(-24), Date(1999, 7, 7)); + assertPred!"=="(date - dur!"hours"(24), Date(1999, 7, 5)); + assertPred!"=="(date - dur!"minutes"(-1440), Date(1999, 7, 7)); + assertPred!"=="(date - dur!"minutes"(1440), Date(1999, 7, 5)); + assertPred!"=="(date - dur!"seconds"(-86_400), Date(1999, 7, 7)); + assertPred!"=="(date - dur!"seconds"(86_400), Date(1999, 7, 5)); + assertPred!"=="(date - dur!"msecs"(-86_400_000), Date(1999, 7, 7)); + assertPred!"=="(date - dur!"msecs"(86_400_000), Date(1999, 7, 5)); + assertPred!"=="(date - dur!"usecs"(-86_400_000_000), Date(1999, 7, 7)); + assertPred!"=="(date - dur!"usecs"(86_400_000_000), Date(1999, 7, 5)); + assertPred!"=="(date - dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 7)); + assertPred!"=="(date - dur!"hnsecs"(864_000_000_000), Date(1999, 7, 5)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(date - TickDuration.from!"usecs"(-86_400_000_000), Date(1999, 7, 7)); + assertPred!"=="(date - TickDuration.from!"usecs"(86_400_000_000), Date(1999, 7, 5)); + } + + auto duration = dur!"days"(12); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date + duration)); + static assert(__traits(compiles, cdate + duration)); + static assert(__traits(compiles, idate + duration)); + + static assert(__traits(compiles, date - duration)); + static assert(__traits(compiles, cdate - duration)); + static assert(__traits(compiles, idate - duration)); + } + + + /++ + Gives the result of adding or subtracting a duration from this Date, as + well as assigning the result to this Date. + + The legal types of arithmetic for Date using this operator are + + $(TABLE + $(TR $(TD Date) $(TD +) $(TD duration) $(TD -->) $(TD Date)) + $(TR $(TD Date) $(TD -) $(TD duration) $(TD -->) $(TD Date)) + ) + + Params: + duration = The duration to add to or subtract from this Date. + +/ + /+ref+/ Date opOpAssign(string op, D)(in D duration) pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + static if(is(Unqual!D == Duration)) + immutable days = duration.total!"days"; + else static if(is(Unqual!D == TickDuration)) + immutable days = convert!("hnsecs", "days")(duration.hnsecs); + + //Ideally, this would just be + //return addDays(unaryFun!(op ~ "a")(days)); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedDays = days; + else static if(op == "-") + immutable signedDays = -days; + else + static assert(0); + + return addDays(signedDays); + } + + unittest + { + assertPred!"+="(Date(1999, 7, 6), dur!"weeks"(7), Date(1999, 8, 24)); + assertPred!"+="(Date(1999, 7, 6), dur!"weeks"(-7), Date(1999, 5, 18)); + assertPred!"+="(Date(1999, 7, 6), dur!"days"(7), Date(1999, 7, 13)); + assertPred!"+="(Date(1999, 7, 6), dur!"days"(-7), Date(1999, 6, 29)); + + assertPred!"+="(Date(1999, 7, 6), dur!"hours"(24), Date(1999, 7, 7)); + assertPred!"+="(Date(1999, 7, 6), dur!"hours"(-24), Date(1999, 7, 5)); + assertPred!"+="(Date(1999, 7, 6), dur!"minutes"(1440), Date(1999, 7, 7)); + assertPred!"+="(Date(1999, 7, 6), dur!"minutes"(-1440), Date(1999, 7, 5)); + assertPred!"+="(Date(1999, 7, 6), dur!"seconds"(86_400), Date(1999, 7, 7)); + assertPred!"+="(Date(1999, 7, 6), dur!"seconds"(-86_400), Date(1999, 7, 5)); + assertPred!"+="(Date(1999, 7, 6), dur!"msecs"(86_400_000), Date(1999, 7, 7)); + assertPred!"+="(Date(1999, 7, 6), dur!"msecs"(-86_400_000), Date(1999, 7, 5)); + assertPred!"+="(Date(1999, 7, 6), dur!"usecs"(86_400_000_000), Date(1999, 7, 7)); + assertPred!"+="(Date(1999, 7, 6), dur!"usecs"(-86_400_000_000), Date(1999, 7, 5)); + assertPred!"+="(Date(1999, 7, 6), dur!"hnsecs"(864_000_000_000), Date(1999, 7, 7)); + assertPred!"+="(Date(1999, 7, 6), dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 5)); + + assertPred!"-="(Date(1999, 7, 6), dur!"weeks"(-7), Date(1999, 8, 24)); + assertPred!"-="(Date(1999, 7, 6), dur!"weeks"(7), Date(1999, 5, 18)); + assertPred!"-="(Date(1999, 7, 6), dur!"days"(-7), Date(1999, 7, 13)); + assertPred!"-="(Date(1999, 7, 6), dur!"days"(7), Date(1999, 6, 29)); + + assertPred!"-="(Date(1999, 7, 6), dur!"hours"(-24), Date(1999, 7, 7)); + assertPred!"-="(Date(1999, 7, 6), dur!"hours"(24), Date(1999, 7, 5)); + assertPred!"-="(Date(1999, 7, 6), dur!"minutes"(-1440), Date(1999, 7, 7)); + assertPred!"-="(Date(1999, 7, 6), dur!"minutes"(1440), Date(1999, 7, 5)); + assertPred!"-="(Date(1999, 7, 6), dur!"seconds"(-86_400), Date(1999, 7, 7)); + assertPred!"-="(Date(1999, 7, 6), dur!"seconds"(86_400), Date(1999, 7, 5)); + assertPred!"-="(Date(1999, 7, 6), dur!"msecs"(-86_400_000), Date(1999, 7, 7)); + assertPred!"-="(Date(1999, 7, 6), dur!"msecs"(86_400_000), Date(1999, 7, 5)); + assertPred!"-="(Date(1999, 7, 6), dur!"usecs"(-86_400_000_000), Date(1999, 7, 7)); + assertPred!"-="(Date(1999, 7, 6), dur!"usecs"(86_400_000_000), Date(1999, 7, 5)); + assertPred!"-="(Date(1999, 7, 6), dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 7)); + assertPred!"-="(Date(1999, 7, 6), dur!"hnsecs"(864_000_000_000), Date(1999, 7, 5)); + + auto duration = dur!"days"(12); + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date += duration)); + static assert(!__traits(compiles, cdate += duration)); + static assert(!__traits(compiles, idate += duration)); + + static assert(__traits(compiles, date -= duration)); + static assert(!__traits(compiles, cdate -= duration)); + static assert(!__traits(compiles, idate -= duration)); + } + + + /++ + Gives the difference between two Dates. + + The legal types of arithmetic for Date using this operator are + + $(TABLE + $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in Date rhs) const pure nothrow + if(op == "-") + { + return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); + } + + unittest + { + auto date = Date(1999, 7, 6); + + assertPred!"=="(Date(1999, 7, 6) - Date(1998, 7, 6), dur!"days"(365)); + assertPred!"=="(Date(1998, 7, 6) - Date(1999, 7, 6), dur!"days"(-365)); + assertPred!"=="(Date(1999, 6, 6) - Date(1999, 5, 6), dur!"days"(31)); + assertPred!"=="(Date(1999, 5, 6) - Date(1999, 6, 6), dur!"days"(-31)); + assertPred!"=="(Date(1999, 1, 1) - Date(1998, 12, 31), dur!"days"(1)); + assertPred!"=="(Date(1998, 12, 31) - Date(1999, 1, 1), dur!"days"(-1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date - date)); + static assert(__traits(compiles, cdate - date)); + static assert(__traits(compiles, idate - date)); + + static assert(__traits(compiles, date - cdate)); + static assert(__traits(compiles, cdate - cdate)); + static assert(__traits(compiles, idate - cdate)); + + static assert(__traits(compiles, date - idate)); + static assert(__traits(compiles, cdate - idate)); + static assert(__traits(compiles, idate - idate)); + } + + + /++ + Returns the difference between the two Dates in months. + + You can get the difference in years by subtracting the year property + of two Dates, and you can get the difference in days or weeks by + subtracting the Dates themselves and using the Duration that results, + but because you cannot convert between months and smaller units without + a specific date (which Durations don't have), you cannot get the difference + in months without doing some math using both the year and month properties, + so this is a convenience function for getting the difference in months. + + Note that the number of days in the months or how far into the month either + date is is irrelevant. It is the difference in the month property combined + with the difference in years * 12. So, for instance, December 31st and + January 1st are one month apart just as December 1st and January 31st are + one month apart. + + Params: + rhs = The Date to subtract from this one. + + Examples: +-------------------- +assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); +assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); +assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); +assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); +-------------------- + +/ + int diffMonths(in Date rhs) const pure nothrow + { + immutable yearDiff = _year - rhs._year; + immutable monthDiff = _month - rhs._month; + + return yearDiff * 12 + monthDiff; + } + + unittest + { + auto date = Date(1999, 7, 6); + + //Test A.D. + assertPred!"=="(date.diffMonths(Date(1998, 6, 5)), 13); + assertPred!"=="(date.diffMonths(Date(1998, 7, 5)), 12); + assertPred!"=="(date.diffMonths(Date(1998, 8, 5)), 11); + assertPred!"=="(date.diffMonths(Date(1998, 9, 5)), 10); + assertPred!"=="(date.diffMonths(Date(1998, 10, 5)), 9); + assertPred!"=="(date.diffMonths(Date(1998, 11, 5)), 8); + assertPred!"=="(date.diffMonths(Date(1998, 12, 5)), 7); + assertPred!"=="(date.diffMonths(Date(1999, 1, 5)), 6); + assertPred!"=="(date.diffMonths(Date(1999, 2, 6)), 5); + assertPred!"=="(date.diffMonths(Date(1999, 3, 6)), 4); + assertPred!"=="(date.diffMonths(Date(1999, 4, 6)), 3); + assertPred!"=="(date.diffMonths(Date(1999, 5, 6)), 2); + assertPred!"=="(date.diffMonths(Date(1999, 6, 6)), 1); + assertPred!"=="(date.diffMonths(date), 0); + assertPred!"=="(date.diffMonths(Date(1999, 8, 6)), -1); + assertPred!"=="(date.diffMonths(Date(1999, 9, 6)), -2); + assertPred!"=="(date.diffMonths(Date(1999, 10, 6)), -3); + assertPred!"=="(date.diffMonths(Date(1999, 11, 6)), -4); + assertPred!"=="(date.diffMonths(Date(1999, 12, 6)), -5); + assertPred!"=="(date.diffMonths(Date(2000, 1, 6)), -6); + assertPred!"=="(date.diffMonths(Date(2000, 2, 6)), -7); + assertPred!"=="(date.diffMonths(Date(2000, 3, 6)), -8); + assertPred!"=="(date.diffMonths(Date(2000, 4, 6)), -9); + assertPred!"=="(date.diffMonths(Date(2000, 5, 6)), -10); + assertPred!"=="(date.diffMonths(Date(2000, 6, 6)), -11); + assertPred!"=="(date.diffMonths(Date(2000, 7, 6)), -12); + assertPred!"=="(date.diffMonths(Date(2000, 8, 6)), -13); + + assertPred!"=="(Date(1998, 6, 5).diffMonths(date), -13); + assertPred!"=="(Date(1998, 7, 5).diffMonths(date), -12); + assertPred!"=="(Date(1998, 8, 5).diffMonths(date), -11); + assertPred!"=="(Date(1998, 9, 5).diffMonths(date), -10); + assertPred!"=="(Date(1998, 10, 5).diffMonths(date), -9); + assertPred!"=="(Date(1998, 11, 5).diffMonths(date), -8); + assertPred!"=="(Date(1998, 12, 5).diffMonths(date), -7); + assertPred!"=="(Date(1999, 1, 5).diffMonths(date), -6); + assertPred!"=="(Date(1999, 2, 6).diffMonths(date), -5); + assertPred!"=="(Date(1999, 3, 6).diffMonths(date), -4); + assertPred!"=="(Date(1999, 4, 6).diffMonths(date), -3); + assertPred!"=="(Date(1999, 5, 6).diffMonths(date), -2); + assertPred!"=="(Date(1999, 6, 6).diffMonths(date), -1); + assertPred!"=="(Date(1999, 8, 6).diffMonths(date), 1); + assertPred!"=="(Date(1999, 9, 6).diffMonths(date), 2); + assertPred!"=="(Date(1999, 10, 6).diffMonths(date), 3); + assertPred!"=="(Date(1999, 11, 6).diffMonths(date), 4); + assertPred!"=="(Date(1999, 12, 6).diffMonths(date), 5); + assertPred!"=="(Date(2000, 1, 6).diffMonths(date), 6); + assertPred!"=="(Date(2000, 2, 6).diffMonths(date), 7); + assertPred!"=="(Date(2000, 3, 6).diffMonths(date), 8); + assertPred!"=="(Date(2000, 4, 6).diffMonths(date), 9); + assertPred!"=="(Date(2000, 5, 6).diffMonths(date), 10); + assertPred!"=="(Date(2000, 6, 6).diffMonths(date), 11); + assertPred!"=="(Date(2000, 7, 6).diffMonths(date), 12); + assertPred!"=="(Date(2000, 8, 6).diffMonths(date), 13); + + assertPred!"=="(date.diffMonths(Date(1999, 6, 30)), 1); + assertPred!"=="(date.diffMonths(Date(1999, 7, 1)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 7, 6)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 7, 11)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 7, 16)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 7, 21)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 7, 26)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 7, 31)), 0); + assertPred!"=="(date.diffMonths(Date(1999, 8, 1)), -1); + + assertPred!"=="(date.diffMonths(Date(1990, 6, 30)), 109); + assertPred!"=="(date.diffMonths(Date(1990, 7, 1)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 7, 6)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 7, 11)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 7, 16)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 7, 21)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 7, 26)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 7, 31)), 108); + assertPred!"=="(date.diffMonths(Date(1990, 8, 1)), 107); + + assertPred!"=="(Date(1999, 6, 30).diffMonths(date), -1); + assertPred!"=="(Date(1999, 7, 1).diffMonths(date), 0); + assertPred!"=="(Date(1999, 7, 6).diffMonths(date), 0); + assertPred!"=="(Date(1999, 7, 11).diffMonths(date), 0); + assertPred!"=="(Date(1999, 7, 16).diffMonths(date), 0); + assertPred!"=="(Date(1999, 7, 21).diffMonths(date), 0); + assertPred!"=="(Date(1999, 7, 26).diffMonths(date), 0); + assertPred!"=="(Date(1999, 7, 31).diffMonths(date), 0); + assertPred!"=="(Date(1999, 8, 1).diffMonths(date), 1); + + assertPred!"=="(Date(1990, 6, 30).diffMonths(date), -109); + assertPred!"=="(Date(1990, 7, 1).diffMonths(date), -108); + assertPred!"=="(Date(1990, 7, 6).diffMonths(date), -108); + assertPred!"=="(Date(1990, 7, 11).diffMonths(date), -108); + assertPred!"=="(Date(1990, 7, 16).diffMonths(date), -108); + assertPred!"=="(Date(1990, 7, 21).diffMonths(date), -108); + assertPred!"=="(Date(1990, 7, 26).diffMonths(date), -108); + assertPred!"=="(Date(1990, 7, 31).diffMonths(date), -108); + assertPred!"=="(Date(1990, 8, 1).diffMonths(date), -107); + + //Test B.C. + auto dateBC = Date(-1999, 7, 6); + + assertPred!"=="(dateBC.diffMonths(Date(-2000, 6, 5)), 13); + assertPred!"=="(dateBC.diffMonths(Date(-2000, 7, 5)), 12); + assertPred!"=="(dateBC.diffMonths(Date(-2000, 8, 5)), 11); + assertPred!"=="(dateBC.diffMonths(Date(-2000, 9, 5)), 10); + assertPred!"=="(dateBC.diffMonths(Date(-2000, 10, 5)), 9); + assertPred!"=="(dateBC.diffMonths(Date(-2000, 11, 5)), 8); + assertPred!"=="(dateBC.diffMonths(Date(-2000, 12, 5)), 7); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 1, 5)), 6); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 2, 6)), 5); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 3, 6)), 4); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 4, 6)), 3); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 5, 6)), 2); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 6, 6)), 1); + assertPred!"=="(dateBC.diffMonths(dateBC), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 8, 6)), -1); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 9, 6)), -2); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 10, 6)), -3); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 11, 6)), -4); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 12, 6)), -5); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 1, 6)), -6); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 2, 6)), -7); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 3, 6)), -8); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 4, 6)), -9); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 5, 6)), -10); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 6, 6)), -11); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 7, 6)), -12); + assertPred!"=="(dateBC.diffMonths(Date(-1998, 8, 6)), -13); + + assertPred!"=="(Date(-2000, 6, 5).diffMonths(dateBC), -13); + assertPred!"=="(Date(-2000, 7, 5).diffMonths(dateBC), -12); + assertPred!"=="(Date(-2000, 8, 5).diffMonths(dateBC), -11); + assertPred!"=="(Date(-2000, 9, 5).diffMonths(dateBC), -10); + assertPred!"=="(Date(-2000, 10, 5).diffMonths(dateBC), -9); + assertPred!"=="(Date(-2000, 11, 5).diffMonths(dateBC), -8); + assertPred!"=="(Date(-2000, 12, 5).diffMonths(dateBC), -7); + assertPred!"=="(Date(-1999, 1, 5).diffMonths(dateBC), -6); + assertPred!"=="(Date(-1999, 2, 6).diffMonths(dateBC), -5); + assertPred!"=="(Date(-1999, 3, 6).diffMonths(dateBC), -4); + assertPred!"=="(Date(-1999, 4, 6).diffMonths(dateBC), -3); + assertPred!"=="(Date(-1999, 5, 6).diffMonths(dateBC), -2); + assertPred!"=="(Date(-1999, 6, 6).diffMonths(dateBC), -1); + assertPred!"=="(Date(-1999, 8, 6).diffMonths(dateBC), 1); + assertPred!"=="(Date(-1999, 9, 6).diffMonths(dateBC), 2); + assertPred!"=="(Date(-1999, 10, 6).diffMonths(dateBC), 3); + assertPred!"=="(Date(-1999, 11, 6).diffMonths(dateBC), 4); + assertPred!"=="(Date(-1999, 12, 6).diffMonths(dateBC), 5); + assertPred!"=="(Date(-1998, 1, 6).diffMonths(dateBC), 6); + assertPred!"=="(Date(-1998, 2, 6).diffMonths(dateBC), 7); + assertPred!"=="(Date(-1998, 3, 6).diffMonths(dateBC), 8); + assertPred!"=="(Date(-1998, 4, 6).diffMonths(dateBC), 9); + assertPred!"=="(Date(-1998, 5, 6).diffMonths(dateBC), 10); + assertPred!"=="(Date(-1998, 6, 6).diffMonths(dateBC), 11); + assertPred!"=="(Date(-1998, 7, 6).diffMonths(dateBC), 12); + assertPred!"=="(Date(-1998, 8, 6).diffMonths(dateBC), 13); + + assertPred!"=="(dateBC.diffMonths(Date(-1999, 6, 30)), 1); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 1)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 6)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 11)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 16)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 21)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 26)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 31)), 0); + assertPred!"=="(dateBC.diffMonths(Date(-1999, 8, 1)), -1); + + assertPred!"=="(dateBC.diffMonths(Date(-2008, 6, 30)), 109); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 1)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 6)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 11)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 16)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 21)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 26)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 31)), 108); + assertPred!"=="(dateBC.diffMonths(Date(-2008, 8, 1)), 107); + + assertPred!"=="(Date(-1999, 6, 30).diffMonths(dateBC), -1); + assertPred!"=="(Date(-1999, 7, 1).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 7, 6).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 7, 11).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 7, 16).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 7, 21).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 7, 26).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 7, 31).diffMonths(dateBC), 0); + assertPred!"=="(Date(-1999, 8, 1).diffMonths(dateBC), 1); + + assertPred!"=="(Date(-2008, 6, 30).diffMonths(dateBC), -109); + assertPred!"=="(Date(-2008, 7, 1).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 7, 6).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 7, 11).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 7, 16).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 7, 21).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 7, 26).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 7, 31).diffMonths(dateBC), -108); + assertPred!"=="(Date(-2008, 8, 1).diffMonths(dateBC), -107); + + //Test Both + assertPred!"=="(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)), 94); + assertPred!"=="(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)), -94); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.diffMonths(date))); + static assert(__traits(compiles, cdate.diffMonths(date))); + static assert(__traits(compiles, idate.diffMonths(date))); + + static assert(__traits(compiles, date.diffMonths(cdate))); + static assert(__traits(compiles, cdate.diffMonths(cdate))); + static assert(__traits(compiles, idate.diffMonths(cdate))); + + static assert(__traits(compiles, date.diffMonths(idate))); + static assert(__traits(compiles, cdate.diffMonths(idate))); + static assert(__traits(compiles, idate.diffMonths(idate))); + + //Verify Examples. + assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); + assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); + assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); + assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); + } + + + /++ + Whether this Date is in a leap year. + +/ + @property bool isLeapYear() const pure nothrow + { + return yearIsLeapYear(_year); + } + + unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, date.isLeapYear = true)); + static assert(!__traits(compiles, cdate.isLeapYear = true)); + static assert(!__traits(compiles, idate.isLeapYear = true)); + } + + + /++ + Day of the week this Date is on. + +/ + @property DayOfWeek dayOfWeek() const pure nothrow + { + return getDayOfWeek(dayOfGregorianCal); + } + + unittest + { + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.dayOfWeek == DayOfWeek.sun)); + static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); + static assert(__traits(compiles, idate.dayOfWeek == DayOfWeek.sun)); + static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); + } + + + /++ + Day of the year this Date is on. + + Examples: +-------------------- +assert(Date(1999, 1, 1).dayOfYear == 1); +assert(Date(1999, 12, 31).dayOfYear == 365); +assert(Date(2000, 12, 31).dayOfYear == 366); +-------------------- + +/ + @property ushort dayOfYear() const pure nothrow + { + switch(_month) + { + case Month.jan: + return _day; + case Month.feb: + return cast(ushort)(31 + _day); + case Month.mar: + return cast(ushort)((isLeapYear ? 60 : 59) + _day); + case Month.apr: + return cast(ushort)((isLeapYear ? 91 : 90) + _day); + case Month.may: + return cast(ushort)((isLeapYear ? 121 : 120) + _day); + case Month.jun: + return cast(ushort)((isLeapYear ? 152 : 151) + _day); + case Month.jul: + return cast(ushort)((isLeapYear ? 182 : 181) + _day); + case Month.aug: + return cast(ushort)((isLeapYear ? 213 : 212) + _day); + case Month.sep: + return cast(ushort)((isLeapYear ? 244 : 243) + _day); + case Month.oct: + return cast(ushort)((isLeapYear ? 274 : 273) + _day); + case Month.nov: + return cast(ushort)((isLeapYear ? 305 : 304) + _day); + case Month.dec: + return cast(ushort)((isLeapYear ? 335 : 334) + _day); + default: + assert(0, "Invalid month."); + } + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(1999, 1, 1).dayOfYear, 1); + assertPred!"=="(Date(1999, 1, 2).dayOfYear, 2); + assertPred!"=="(Date(1999, 1, 3).dayOfYear, 3); + assertPred!"=="(Date(1999, 1, 31).dayOfYear, 31); + assertPred!"=="(Date(1999, 2, 1).dayOfYear, 32); + assertPred!"=="(Date(1999, 2, 28).dayOfYear, 59); + assertPred!"=="(Date(1999, 3, 1).dayOfYear, 60); + assertPred!"=="(Date(1999, 3, 31).dayOfYear, 90); + assertPred!"=="(Date(1999, 4, 1).dayOfYear, 91); + assertPred!"=="(Date(1999, 4, 30).dayOfYear, 120); + assertPred!"=="(Date(1999, 5, 1).dayOfYear, 121); + assertPred!"=="(Date(1999, 5, 31).dayOfYear, 151); + assertPred!"=="(Date(1999, 6, 1).dayOfYear, 152); + assertPred!"=="(Date(1999, 6, 30).dayOfYear, 181); + assertPred!"=="(Date(1999, 7, 1).dayOfYear, 182); + assertPred!"=="(Date(1999, 7, 31).dayOfYear, 212); + assertPred!"=="(Date(1999, 8, 1).dayOfYear, 213); + assertPred!"=="(Date(1999, 8, 31).dayOfYear, 243); + assertPred!"=="(Date(1999, 9, 1).dayOfYear, 244); + assertPred!"=="(Date(1999, 9, 30).dayOfYear, 273); + assertPred!"=="(Date(1999, 10, 1).dayOfYear, 274); + assertPred!"=="(Date(1999, 10, 31).dayOfYear, 304); + assertPred!"=="(Date(1999, 11, 1).dayOfYear, 305); + assertPred!"=="(Date(1999, 11, 30).dayOfYear, 334); + assertPred!"=="(Date(1999, 12, 1).dayOfYear, 335); + assertPred!"=="(Date(1999, 12, 31).dayOfYear, 365); + + assertPred!"=="(Date(2000, 1, 1).dayOfYear, 1); + assertPred!"=="(Date(2000, 2, 1).dayOfYear, 32); + assertPred!"=="(Date(2000, 2, 29).dayOfYear, 60); + assertPred!"=="(Date(2000, 3, 1).dayOfYear, 61); + assertPred!"=="(Date(2000, 3, 31).dayOfYear, 91); + assertPred!"=="(Date(2000, 4, 1).dayOfYear, 92); + assertPred!"=="(Date(2000, 4, 30).dayOfYear, 121); + assertPred!"=="(Date(2000, 5, 1).dayOfYear, 122); + assertPred!"=="(Date(2000, 5, 31).dayOfYear, 152); + assertPred!"=="(Date(2000, 6, 1).dayOfYear, 153); + assertPred!"=="(Date(2000, 6, 30).dayOfYear, 182); + assertPred!"=="(Date(2000, 7, 1).dayOfYear, 183); + assertPred!"=="(Date(2000, 7, 31).dayOfYear, 213); + assertPred!"=="(Date(2000, 8, 1).dayOfYear, 214); + assertPred!"=="(Date(2000, 8, 31).dayOfYear, 244); + assertPred!"=="(Date(2000, 9, 1).dayOfYear, 245); + assertPred!"=="(Date(2000, 9, 30).dayOfYear, 274); + assertPred!"=="(Date(2000, 10, 1).dayOfYear, 275); + assertPred!"=="(Date(2000, 10, 31).dayOfYear, 305); + assertPred!"=="(Date(2000, 11, 1).dayOfYear, 306); + assertPred!"=="(Date(2000, 11, 30).dayOfYear, 335); + assertPred!"=="(Date(2000, 12, 1).dayOfYear, 336); + assertPred!"=="(Date(2000, 12, 31).dayOfYear, 366); + + //Test B.C. + assertPred!"=="(Date(0, 1, 1).dayOfYear, 1); + assertPred!"=="(Date(0, 12, 31).dayOfYear, 366); + assertPred!"=="(Date(1, 1, 1).dayOfYear, 1); + assertPred!"=="(Date(1, 12, 31).dayOfYear, 365); + assertPred!"=="(Date(-1, 1, 1).dayOfYear, 1); + assertPred!"=="(Date(-1, 12, 31).dayOfYear, 365); + assertPred!"=="(Date(4, 1, 1).dayOfYear, 1); + assertPred!"=="(Date(4, 12, 31).dayOfYear, 366); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.dayOfYear == 187)); + static assert(__traits(compiles, idate.dayOfYear == 187)); + + //Verify Examples. + assert(Date(1999, 1, 1).dayOfYear == 1); + assert(Date(1999, 12, 31).dayOfYear == 365); + assert(Date(2000, 12, 31).dayOfYear == 366); + } + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this Date is on. + + Throws: + DateTimeException if the given day is an invalid day of the year. + +/ + @property void dayOfYear(int day) pure + { + if(isLeapYear) + { + if(day <= 0 || day > daysInLeapYear) + throw new DateTimeException("Invalid day of the year."); + + switch(day) + { + case 1: .. case 31: + { + _month = Month.jan; + _day = cast(ubyte)day; + break; + } + case 32: .. case 60: + { + _month = Month.feb; + _day = cast(ubyte)(day - 31); + break; + } + case 61: .. case 91: + { + _month = Month.mar; + _day = cast(ubyte)(day - 60); + break; + } + case 92: .. case 121: + { + _month = Month.apr; + _day = cast(ubyte)(day - 91); + break; + } + case 122: .. case 152: + { + _month = Month.may; + _day = cast(ubyte)(day - 121); + break; + } + case 153: .. case 182: + { + _month = Month.jun; + _day = cast(ubyte)(day - 152); + break; + } + case 183: .. case 213: + { + _month = Month.jul; + _day = cast(ubyte)(day - 182); + break; + } + case 214: .. case 244: + { + _month = Month.aug; + _day = cast(ubyte)(day - 213); + break; + } + case 245: .. case 274: + { + _month = Month.sep; + _day = cast(ubyte)(day - 244); + break; + } + case 275: .. case 305: + { + _month = Month.oct; + _day = cast(ubyte)(day - 274); + break; + } + case 306: .. case 335: + { + _month = Month.nov; + _day = cast(ubyte)(day - 305); + break; + } + case 336: .. case 366: + { + _month = Month.dec; + _day = cast(ubyte)(day - 335); + break; + } + default: + assert(0, "Invalid day of the year."); + } + } + else + { + if(day <= 0 || day > daysInYear) + throw new DateTimeException("Invalid day of the year."); + + switch(day) + { + case 1: .. case 31: + { + _month = Month.jan; + _day = cast(ubyte)day; + break; + } + case 32: .. case 59: + { + _month = Month.feb; + _day = cast(ubyte)(day - 31); + break; + } + case 60: .. case 90: + { + _month = Month.mar; + _day = cast(ubyte)(day - 59); + break; + } + case 91: .. case 120: + { + _month = Month.apr; + _day = cast(ubyte)(day - 90); + break; + } + case 121: .. case 151: + { + _month = Month.may; + _day = cast(ubyte)(day - 120); + break; + } + case 152: .. case 181: + { + _month = Month.jun; + _day = cast(ubyte)(day - 151); + break; + } + case 182: .. case 212: + { + _month = Month.jul; + _day = cast(ubyte)(day - 181); + break; + } + case 213: .. case 243: + { + _month = Month.aug; + _day = cast(ubyte)(day - 212); + break; + } + case 244: .. case 273: + { + _month = Month.sep; + _day = cast(ubyte)(day - 243); + break; + } + case 274: .. case 304: + { + _month = Month.oct; + _day = cast(ubyte)(day - 273); + break; + } + case 305: .. case 334: + { + _month = Month.nov; + _day = cast(ubyte)(day - 304); + break; + } + case 335: .. case 365: + { + _month = Month.dec; + _day = cast(ubyte)(day - 334); + break; + } + default: + assert(0, "Invalid day of the year."); + } + } + } + + unittest + { + auto date = Date(1999, 1, 1); + + void testFunc(int day, int expectedMonth, int expectedDay, size_t line = __LINE__) + { + date.dayOfYear = day; + assertPred!"=="(date.month, expectedMonth, "", __FILE__, line); + assertPred!"=="(date.day, expectedDay, "", __FILE__, line); + } + + //Test A.D. + testFunc(1, 1, 1); + testFunc(2, 1, 2); + testFunc(3, 1, 3); + testFunc(31, 1, 31); + + testFunc(32, 2, 1); + testFunc(59, 2, 28); + testFunc(60, 3, 1); + testFunc(90, 3, 31); + testFunc(91, 4, 1); + testFunc(120, 4, 30); + testFunc(121, 5, 1); + testFunc(151, 5, 31); + testFunc(152, 6, 1); + testFunc(181, 6, 30); + testFunc(182, 7, 1); + testFunc(212, 7, 31); + testFunc(213, 8, 1); + testFunc(243, 8, 31); + testFunc(244, 9, 1); + testFunc(273, 9, 30); + testFunc(274, 10, 1); + testFunc(304, 10, 31); + testFunc(305, 11, 1); + testFunc(334, 11, 30); + testFunc(335, 12, 1); + testFunc(365, 12, 31); + + date.year = 2000; + testFunc(1, 1, 1); + testFunc(32, 2, 1); + testFunc(60, 2, 29); + testFunc(61, 3, 1); + testFunc(91, 3, 31); + testFunc(92, 4, 1); + testFunc(121, 4, 30); + testFunc(122, 5, 1); + testFunc(152, 5, 31); + testFunc(153, 6, 1); + testFunc(182, 6, 30); + testFunc(183, 7, 1); + testFunc(213, 7, 31); + testFunc(214, 8, 1); + testFunc(244, 8, 31); + testFunc(245, 9, 1); + testFunc(274, 9, 30); + testFunc(275, 10, 1); + testFunc(305, 10, 31); + testFunc(306, 11, 1); + testFunc(335, 11, 30); + testFunc(336, 12, 1); + testFunc(366, 12, 31); + + //Test B.C. + date.year = 0; + testFunc(1, 1, 1); + testFunc(32, 2, 1); + testFunc(60, 2, 29); + testFunc(61, 3, 1); + testFunc(91, 3, 31); + testFunc(92, 4, 1); + testFunc(121, 4, 30); + testFunc(122, 5, 1); + testFunc(152, 5, 31); + testFunc(153, 6, 1); + testFunc(182, 6, 30); + testFunc(183, 7, 1); + testFunc(213, 7, 31); + testFunc(214, 8, 1); + testFunc(244, 8, 31); + testFunc(245, 9, 1); + testFunc(274, 9, 30); + testFunc(275, 10, 1); + testFunc(305, 10, 31); + testFunc(306, 11, 1); + testFunc(335, 11, 30); + testFunc(336, 12, 1); + testFunc(366, 12, 31); + + date.year = -1; + testFunc(1, 1, 1); + testFunc(2, 1, 2); + testFunc(3, 1, 3); + testFunc(31, 1, 31); + + testFunc(32, 2, 1); + testFunc(59, 2, 28); + testFunc(60, 3, 1); + testFunc(90, 3, 31); + testFunc(91, 4, 1); + testFunc(120, 4, 30); + testFunc(121, 5, 1); + testFunc(151, 5, 31); + testFunc(152, 6, 1); + testFunc(181, 6, 30); + testFunc(182, 7, 1); + testFunc(212, 7, 31); + testFunc(213, 8, 1); + testFunc(243, 8, 31); + testFunc(244, 9, 1); + testFunc(273, 9, 30); + testFunc(274, 10, 1); + testFunc(304, 10, 31); + testFunc(305, 11, 1); + testFunc(334, 11, 30); + testFunc(335, 12, 1); + testFunc(365, 12, 31); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.dayOfYear = 187)); + static assert(!__traits(compiles, idate.dayOfYear = 187)); + } + + + /++ + The Xth day of the Gregorian Calendar that this Date is on. + + Examples: +-------------------- +assert(Date(1, 1, 1).dayOfGregorianCal == 1); +assert(Date(1, 12, 31).dayOfGregorianCal == 365); +assert(Date(2, 1, 1).dayOfGregorianCal == 366); + +assert(Date(0, 12, 31).dayOfGregorianCal == 0); +assert(Date(0, 1, 1).dayOfGregorianCal == -365); +assert(Date(-1, 12, 31).dayOfGregorianCal == -366); + +assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); +assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); +-------------------- + +/ + @property int dayOfGregorianCal() const pure nothrow + { + if(isAD) + { + if(_year == 1) + return dayOfYear; + + int years = _year - 1; + auto days = (years / 400) * daysIn400Years; + years %= 400; + + days += (years / 100) * daysIn100Years; + years %= 100; + + days += (years / 4) * daysIn4Years; + years %= 4; + + days += years * daysInYear; + + days += dayOfYear; + + return days; + } + else if(_year == 0) + return dayOfYear - daysInLeapYear; + else + { + int years = _year; + auto days = (years / 400) * daysIn400Years; + years %= 400; + + days += (years / 100) * daysIn100Years; + years %= 100; + + days += (years / 4) * daysIn4Years; + years %= 4; + + if(years < 0) + { + days -= daysInLeapYear; + ++years; + + days += years * daysInYear; + + days -= daysInYear - dayOfYear; + } + else + days -= daysInLeapYear - dayOfYear; + + return days; + } + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(1, 1, 1).dayOfGregorianCal, 1); + assertPred!"=="(Date(1, 1, 2).dayOfGregorianCal, 2); + assertPred!"=="(Date(1, 2, 1).dayOfGregorianCal, 32); + assertPred!"=="(Date(2, 1, 1).dayOfGregorianCal, 366); + assertPred!"=="(Date(3, 1, 1).dayOfGregorianCal, 731); + assertPred!"=="(Date(4, 1, 1).dayOfGregorianCal, 1096); + assertPred!"=="(Date(5, 1, 1).dayOfGregorianCal, 1462); + assertPred!"=="(Date(50, 1, 1).dayOfGregorianCal, 17_898); + assertPred!"=="(Date(97, 1, 1).dayOfGregorianCal, 35_065); + assertPred!"=="(Date(100, 1, 1).dayOfGregorianCal, 36_160); + assertPred!"=="(Date(101, 1, 1).dayOfGregorianCal, 36_525); + assertPred!"=="(Date(105, 1, 1).dayOfGregorianCal, 37_986); + assertPred!"=="(Date(200, 1, 1).dayOfGregorianCal, 72_684); + assertPred!"=="(Date(201, 1, 1).dayOfGregorianCal, 73_049); + assertPred!"=="(Date(300, 1, 1).dayOfGregorianCal, 109_208); + assertPred!"=="(Date(301, 1, 1).dayOfGregorianCal, 109_573); + assertPred!"=="(Date(400, 1, 1).dayOfGregorianCal, 145_732); + assertPred!"=="(Date(401, 1, 1).dayOfGregorianCal, 146_098); + assertPred!"=="(Date(500, 1, 1).dayOfGregorianCal, 182_257); + assertPred!"=="(Date(501, 1, 1).dayOfGregorianCal, 182_622); + assertPred!"=="(Date(1000, 1, 1).dayOfGregorianCal, 364_878); + assertPred!"=="(Date(1001, 1, 1).dayOfGregorianCal, 365_243); + assertPred!"=="(Date(1600, 1, 1).dayOfGregorianCal, 584_023); + assertPred!"=="(Date(1601, 1, 1).dayOfGregorianCal, 584_389); + assertPred!"=="(Date(1900, 1, 1).dayOfGregorianCal, 693_596); + assertPred!"=="(Date(1901, 1, 1).dayOfGregorianCal, 693_961); + assertPred!"=="(Date(1945, 11, 12).dayOfGregorianCal, 710_347); + assertPred!"=="(Date(1999, 1, 1).dayOfGregorianCal, 729_755); + assertPred!"=="(Date(2000, 1, 1).dayOfGregorianCal, 730_120); + assertPred!"=="(Date(2001, 1, 1).dayOfGregorianCal, 730_486); + + assertPred!"=="(Date(2010, 1, 1).dayOfGregorianCal, 733_773); + assertPred!"=="(Date(2010, 1, 31).dayOfGregorianCal, 733_803); + assertPred!"=="(Date(2010, 2, 1).dayOfGregorianCal, 733_804); + assertPred!"=="(Date(2010, 2, 28).dayOfGregorianCal, 733_831); + assertPred!"=="(Date(2010, 3, 1).dayOfGregorianCal, 733_832); + assertPred!"=="(Date(2010, 3, 31).dayOfGregorianCal, 733_862); + assertPred!"=="(Date(2010, 4, 1).dayOfGregorianCal, 733_863); + assertPred!"=="(Date(2010, 4, 30).dayOfGregorianCal, 733_892); + assertPred!"=="(Date(2010, 5, 1).dayOfGregorianCal, 733_893); + assertPred!"=="(Date(2010, 5, 31).dayOfGregorianCal, 733_923); + assertPred!"=="(Date(2010, 6, 1).dayOfGregorianCal, 733_924); + assertPred!"=="(Date(2010, 6, 30).dayOfGregorianCal, 733_953); + assertPred!"=="(Date(2010, 7, 1).dayOfGregorianCal, 733_954); + assertPred!"=="(Date(2010, 7, 31).dayOfGregorianCal, 733_984); + assertPred!"=="(Date(2010, 8, 1).dayOfGregorianCal, 733_985); + assertPred!"=="(Date(2010, 8, 31).dayOfGregorianCal, 734_015); + assertPred!"=="(Date(2010, 9, 1).dayOfGregorianCal, 734_016); + assertPred!"=="(Date(2010, 9, 30).dayOfGregorianCal, 734_045); + assertPred!"=="(Date(2010, 10, 1).dayOfGregorianCal, 734_046); + assertPred!"=="(Date(2010, 10, 31).dayOfGregorianCal, 734_076); + assertPred!"=="(Date(2010, 11, 1).dayOfGregorianCal, 734_077); + assertPred!"=="(Date(2010, 11, 30).dayOfGregorianCal, 734_106); + assertPred!"=="(Date(2010, 12, 1).dayOfGregorianCal, 734_107); + assertPred!"=="(Date(2010, 12, 31).dayOfGregorianCal, 734_137); + + assertPred!"=="(Date(2012, 2, 1).dayOfGregorianCal, 734_534); + assertPred!"=="(Date(2012, 2, 28).dayOfGregorianCal, 734_561); + assertPred!"=="(Date(2012, 2, 29).dayOfGregorianCal, 734_562); + assertPred!"=="(Date(2012, 3, 1).dayOfGregorianCal, 734_563); + + //Test B.C. + assertPred!"=="(Date(0, 12, 31).dayOfGregorianCal, 0); + assertPred!"=="(Date(0, 12, 30).dayOfGregorianCal, -1); + assertPred!"=="(Date(0, 12, 1).dayOfGregorianCal, -30); + assertPred!"=="(Date(0, 11, 30).dayOfGregorianCal, -31); + + assertPred!"=="(Date(-1, 12, 31).dayOfGregorianCal, -366); + assertPred!"=="(Date(-1, 12, 30).dayOfGregorianCal, -367); + assertPred!"=="(Date(-1, 1, 1).dayOfGregorianCal, -730); + assertPred!"=="(Date(-2, 12, 31).dayOfGregorianCal, -731); + assertPred!"=="(Date(-2, 1, 1).dayOfGregorianCal, -1095); + assertPred!"=="(Date(-3, 12, 31).dayOfGregorianCal, -1096); + assertPred!"=="(Date(-3, 1, 1).dayOfGregorianCal, -1460); + assertPred!"=="(Date(-4, 12, 31).dayOfGregorianCal, -1461); + assertPred!"=="(Date(-4, 1, 1).dayOfGregorianCal, -1826); + assertPred!"=="(Date(-5, 12, 31).dayOfGregorianCal, -1827); + assertPred!"=="(Date(-5, 1, 1).dayOfGregorianCal, -2191); + assertPred!"=="(Date(-9, 1, 1).dayOfGregorianCal, -3652); + + assertPred!"=="(Date(-49, 1, 1).dayOfGregorianCal, -18_262); + assertPred!"=="(Date(-50, 1, 1).dayOfGregorianCal, -18_627); + assertPred!"=="(Date(-97, 1, 1).dayOfGregorianCal, -35_794); + assertPred!"=="(Date(-99, 12, 31).dayOfGregorianCal, -36_160); + assertPred!"=="(Date(-99, 1, 1).dayOfGregorianCal, -36_524); + assertPred!"=="(Date(-100, 1, 1).dayOfGregorianCal, -36_889); + assertPred!"=="(Date(-101, 1, 1).dayOfGregorianCal, -37_254); + assertPred!"=="(Date(-105, 1, 1).dayOfGregorianCal, -38_715); + assertPred!"=="(Date(-200, 1, 1).dayOfGregorianCal, -73_413); + assertPred!"=="(Date(-201, 1, 1).dayOfGregorianCal, -73_778); + assertPred!"=="(Date(-300, 1, 1).dayOfGregorianCal, -109_937); + assertPred!"=="(Date(-301, 1, 1).dayOfGregorianCal, -110_302); + assertPred!"=="(Date(-400, 12, 31).dayOfGregorianCal, -146_097); + assertPred!"=="(Date(-400, 1, 1).dayOfGregorianCal, -146_462); + assertPred!"=="(Date(-401, 1, 1).dayOfGregorianCal, -146_827); + assertPred!"=="(Date(-499, 1, 1).dayOfGregorianCal, -182_621); + assertPred!"=="(Date(-500, 1, 1).dayOfGregorianCal, -182_986); + assertPred!"=="(Date(-501, 1, 1).dayOfGregorianCal, -183_351); + assertPred!"=="(Date(-1000, 1, 1).dayOfGregorianCal, -365_607); + assertPred!"=="(Date(-1001, 1, 1).dayOfGregorianCal, -365_972); + assertPred!"=="(Date(-1599, 1, 1).dayOfGregorianCal, -584_387); + assertPred!"=="(Date(-1600, 12, 31).dayOfGregorianCal, -584_388); + assertPred!"=="(Date(-1600, 1, 1).dayOfGregorianCal, -584_753); + assertPred!"=="(Date(-1601, 1, 1).dayOfGregorianCal, -585_118); + assertPred!"=="(Date(-1900, 1, 1).dayOfGregorianCal, -694_325); + assertPred!"=="(Date(-1901, 1, 1).dayOfGregorianCal, -694_690); + assertPred!"=="(Date(-1999, 1, 1).dayOfGregorianCal, -730_484); + assertPred!"=="(Date(-2000, 12, 31).dayOfGregorianCal, -730_485); + assertPred!"=="(Date(-2000, 1, 1).dayOfGregorianCal, -730_850); + assertPred!"=="(Date(-2001, 1, 1).dayOfGregorianCal, -731_215); + + assertPred!"=="(Date(-2010, 1, 1).dayOfGregorianCal, -734_502); + assertPred!"=="(Date(-2010, 1, 31).dayOfGregorianCal, -734_472); + assertPred!"=="(Date(-2010, 2, 1).dayOfGregorianCal, -734_471); + assertPred!"=="(Date(-2010, 2, 28).dayOfGregorianCal, -734_444); + assertPred!"=="(Date(-2010, 3, 1).dayOfGregorianCal, -734_443); + assertPred!"=="(Date(-2010, 3, 31).dayOfGregorianCal, -734_413); + assertPred!"=="(Date(-2010, 4, 1).dayOfGregorianCal, -734_412); + assertPred!"=="(Date(-2010, 4, 30).dayOfGregorianCal, -734_383); + assertPred!"=="(Date(-2010, 5, 1).dayOfGregorianCal, -734_382); + assertPred!"=="(Date(-2010, 5, 31).dayOfGregorianCal, -734_352); + assertPred!"=="(Date(-2010, 6, 1).dayOfGregorianCal, -734_351); + assertPred!"=="(Date(-2010, 6, 30).dayOfGregorianCal, -734_322); + assertPred!"=="(Date(-2010, 7, 1).dayOfGregorianCal, -734_321); + assertPred!"=="(Date(-2010, 7, 31).dayOfGregorianCal, -734_291); + assertPred!"=="(Date(-2010, 8, 1).dayOfGregorianCal, -734_290); + assertPred!"=="(Date(-2010, 8, 31).dayOfGregorianCal, -734_260); + assertPred!"=="(Date(-2010, 9, 1).dayOfGregorianCal, -734_259); + assertPred!"=="(Date(-2010, 9, 30).dayOfGregorianCal, -734_230); + assertPred!"=="(Date(-2010, 10, 1).dayOfGregorianCal, -734_229); + assertPred!"=="(Date(-2010, 10, 31).dayOfGregorianCal, -734_199); + assertPred!"=="(Date(-2010, 11, 1).dayOfGregorianCal, -734_198); + assertPred!"=="(Date(-2010, 11, 30).dayOfGregorianCal, -734_169); + assertPred!"=="(Date(-2010, 12, 1).dayOfGregorianCal, -734_168); + assertPred!"=="(Date(-2010, 12, 31).dayOfGregorianCal, -734_138); + + assertPred!"=="(Date(-2012, 2, 1).dayOfGregorianCal, -735_202); + assertPred!"=="(Date(-2012, 2, 28).dayOfGregorianCal, -735_175); + assertPred!"=="(Date(-2012, 2, 29).dayOfGregorianCal, -735_174); + assertPred!"=="(Date(-2012, 3, 1).dayOfGregorianCal, -735_173); + + assertPred!"=="(Date(-3760, 9, 7).dayOfGregorianCal, -1_373_427); //Start of Hebrew Calendar + + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.dayOfGregorianCal)); + static assert(__traits(compiles, cdate.dayOfGregorianCal)); + static assert(__traits(compiles, idate.dayOfGregorianCal)); + + //Verify Examples. + assert(Date(1, 1, 1).dayOfGregorianCal == 1); + assert(Date(1, 12, 31).dayOfGregorianCal == 365); + assert(Date(2, 1, 1).dayOfGregorianCal == 366); + + assert(Date(0, 12, 31).dayOfGregorianCal == 0); + assert(Date(0, 1, 1).dayOfGregorianCal == -365); + assert(Date(-1, 12, 31).dayOfGregorianCal == -366); + + assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); + assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); + } + + /++ + The Xth day of the Gregorian Calendar that this Date is on. + + Params: + day = The day of the Gregorian Calendar to set this Date to. + + Examples: +-------------------- +auto date = Date.init; +date.dayOfGregorianCal = 1; +assert(date == Date(1, 1, 1)); + +date.dayOfGregorianCal = 365; +assert(date == Date(1, 12, 31)); + +date.dayOfGregorianCal = 366; +assert(date == Date(2, 1, 1)); + +date.dayOfGregorianCal = 0; +assert(date == Date(0, 12, 31)); + +date.dayOfGregorianCal = -365; +assert(date == Date(-0, 1, 1)); + +date.dayOfGregorianCal = -366; +assert(date == Date(-1, 12, 31)); + +date.dayOfGregorianCal = 730_120; +assert(date == Date(2000, 1, 1)); + +date.dayOfGregorianCal = 734_137; +assert(date == Date(2010, 12, 31)); +-------------------- + +/ + @property void dayOfGregorianCal(int day) pure nothrow + { + this = Date(day); + } + + unittest + { + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.dayOfGregorianCal = 187)); + static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); + static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); + } + + //Verify Examples. + { + auto date = Date.init; + date.dayOfGregorianCal = 1; + assert(date == Date(1, 1, 1)); + + date.dayOfGregorianCal = 365; + assert(date == Date(1, 12, 31)); + + date.dayOfGregorianCal = 366; + assert(date == Date(2, 1, 1)); + + date.dayOfGregorianCal = 0; + assert(date == Date(0, 12, 31)); + + date.dayOfGregorianCal = -365; + assert(date == Date(-0, 1, 1)); + + date.dayOfGregorianCal = -366; + assert(date == Date(-1, 12, 31)); + + date.dayOfGregorianCal = 730_120; + assert(date == Date(2000, 1, 1)); + + date.dayOfGregorianCal = 734_137; + assert(date == Date(2010, 12, 31)); + } + } + + + /++ + The ISO 8601 week of the year that this Date is in. + + See_Also: + ISO Week Date + +/ + @property ubyte isoWeek() const pure nothrow + { + immutable weekday = dayOfWeek; + immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; + immutable week = (dayOfYear - adjustedWeekday + 10) / 7; + + try + { + if(week == 53) + { + switch(Date(_year + 1, 1, 1).dayOfWeek) + { + case DayOfWeek.mon: + case DayOfWeek.tue: + case DayOfWeek.wed: + case DayOfWeek.thu: + return 1; + case DayOfWeek.fri: + case DayOfWeek.sat: + case DayOfWeek.sun: + return 53; + default: + assert(0, "Invalid ISO Week"); + } + } + else if(week > 0) + return cast(ubyte)week; + else + return Date(_year - 1, 12, 31).isoWeek; + } + catch(Exception e) + assert(0, "Date's constructor threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(2009, 12, 28).isoWeek, 53); + assertPred!"=="(Date(2009, 12, 29).isoWeek, 53); + assertPred!"=="(Date(2009, 12, 30).isoWeek, 53); + assertPred!"=="(Date(2009, 12, 31).isoWeek, 53); + assertPred!"=="(Date(2010, 1, 1).isoWeek, 53); + assertPred!"=="(Date(2010, 1, 2).isoWeek, 53); + assertPred!"=="(Date(2010, 1, 3).isoWeek, 53); + assertPred!"=="(Date(2010, 1, 4).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 5).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 6).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 7).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 8).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 9).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 10).isoWeek, 1); + assertPred!"=="(Date(2010, 1, 11).isoWeek, 2); + assertPred!"=="(Date(2010, 12, 31).isoWeek, 52); + + assertPred!"=="(Date(2004, 12, 26).isoWeek, 52); + assertPred!"=="(Date(2004, 12, 27).isoWeek, 53); + assertPred!"=="(Date(2004, 12, 28).isoWeek, 53); + assertPred!"=="(Date(2004, 12, 29).isoWeek, 53); + assertPred!"=="(Date(2004, 12, 30).isoWeek, 53); + assertPred!"=="(Date(2004, 12, 31).isoWeek, 53); + assertPred!"=="(Date(2005, 1, 1).isoWeek, 53); + assertPred!"=="(Date(2005, 1, 2).isoWeek, 53); + + assertPred!"=="(Date(2005, 12, 31).isoWeek, 52); + assertPred!"=="(Date(2007, 1, 1).isoWeek, 1); + + assertPred!"=="(Date(2007, 12, 30).isoWeek, 52); + assertPred!"=="(Date(2007, 12, 31).isoWeek, 1); + assertPred!"=="(Date(2008, 1, 1).isoWeek, 1); + + assertPred!"=="(Date(2008, 12, 28).isoWeek, 52); + assertPred!"=="(Date(2008, 12, 29).isoWeek, 1); + assertPred!"=="(Date(2008, 12, 30).isoWeek, 1); + assertPred!"=="(Date(2008, 12, 31).isoWeek, 1); + assertPred!"=="(Date(2009, 1, 1).isoWeek, 1); + assertPred!"=="(Date(2009, 1, 2).isoWeek, 1); + assertPred!"=="(Date(2009, 1, 3).isoWeek, 1); + assertPred!"=="(Date(2009, 1, 4).isoWeek, 1); + + //Test B.C. + //The algorithm should work identically for both A.D. and B.C. since + //it doesn't really take the year into account, so B.C. testing + //probably isn't really needed. + assertPred!"=="(Date(0, 12, 31).isoWeek, 52); + assertPred!"=="(Date(0, 1, 4).isoWeek, 1); + assertPred!"=="(Date(0, 1, 1).isoWeek, 52); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.isoWeek == 3)); + static assert(!__traits(compiles, cdate.isoWeek = 3)); + static assert(__traits(compiles, idate.isoWeek == 3)); + static assert(!__traits(compiles, idate.isoWeek = 3)); + } + + + /++ + Date for the last day in the month that this Date is in. + + Examples: +-------------------- +assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); +assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); +assert(Date(2000, 2, 7).endOfMonth == Date(1999, 2, 29)); +assert(Date(2000, 6, 4).endOfMonth == Date(1999, 6, 30)); +-------------------- + +/ + @property Date endOfMonth() const pure nothrow + { + try + return Date(_year, _month, maxDay(_year, _month)); + catch(Exception e) + assert(0, "Date's constructor threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(1999, 1, 1).endOfMonth, Date(1999, 1, 31)); + assertPred!"=="(Date(1999, 2, 1).endOfMonth, Date(1999, 2, 28)); + assertPred!"=="(Date(2000, 2, 1).endOfMonth, Date(2000, 2, 29)); + assertPred!"=="(Date(1999, 3, 1).endOfMonth, Date(1999, 3, 31)); + assertPred!"=="(Date(1999, 4, 1).endOfMonth, Date(1999, 4, 30)); + assertPred!"=="(Date(1999, 5, 1).endOfMonth, Date(1999, 5, 31)); + assertPred!"=="(Date(1999, 6, 1).endOfMonth, Date(1999, 6, 30)); + assertPred!"=="(Date(1999, 7, 1).endOfMonth, Date(1999, 7, 31)); + assertPred!"=="(Date(1999, 8, 1).endOfMonth, Date(1999, 8, 31)); + assertPred!"=="(Date(1999, 9, 1).endOfMonth, Date(1999, 9, 30)); + assertPred!"=="(Date(1999, 10, 1).endOfMonth, Date(1999, 10, 31)); + assertPred!"=="(Date(1999, 11, 1).endOfMonth, Date(1999, 11, 30)); + assertPred!"=="(Date(1999, 12, 1).endOfMonth, Date(1999, 12, 31)); + + //Test B.C. + assertPred!"=="(Date(-1999, 1, 1).endOfMonth, Date(-1999, 1, 31)); + assertPred!"=="(Date(-1999, 2, 1).endOfMonth, Date(-1999, 2, 28)); + assertPred!"=="(Date(-2000, 2, 1).endOfMonth, Date(-2000, 2, 29)); + assertPred!"=="(Date(-1999, 3, 1).endOfMonth, Date(-1999, 3, 31)); + assertPred!"=="(Date(-1999, 4, 1).endOfMonth, Date(-1999, 4, 30)); + assertPred!"=="(Date(-1999, 5, 1).endOfMonth, Date(-1999, 5, 31)); + assertPred!"=="(Date(-1999, 6, 1).endOfMonth, Date(-1999, 6, 30)); + assertPred!"=="(Date(-1999, 7, 1).endOfMonth, Date(-1999, 7, 31)); + assertPred!"=="(Date(-1999, 8, 1).endOfMonth, Date(-1999, 8, 31)); + assertPred!"=="(Date(-1999, 9, 1).endOfMonth, Date(-1999, 9, 30)); + assertPred!"=="(Date(-1999, 10, 1).endOfMonth, Date(-1999, 10, 31)); + assertPred!"=="(Date(-1999, 11, 1).endOfMonth, Date(-1999, 11, 30)); + assertPred!"=="(Date(-1999, 12, 1).endOfMonth, Date(-1999, 12, 31)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); + static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); + + //Verify Examples. + assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); + assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); + } + + + /++ + The last day in the month that this Date is in. + + Examples: +-------------------- +assert(Date(1999, 1, 6).endOfMonthDay == 31); +assert(Date(1999, 2, 7).endOfMonthDay == 28); +assert(Date(2000, 2, 7).endOfMonthDay == 29); +assert(Date(2000, 6, 4).endOfMonthDay == 30); +-------------------- + +/ + @property ubyte endOfMonthDay() const pure nothrow + { + return maxDay(_year, _month); + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(1999, 1, 1).endOfMonthDay, 31); + assertPred!"=="(Date(1999, 2, 1).endOfMonthDay, 28); + assertPred!"=="(Date(2000, 2, 1).endOfMonthDay, 29); + assertPred!"=="(Date(1999, 3, 1).endOfMonthDay, 31); + assertPred!"=="(Date(1999, 4, 1).endOfMonthDay, 30); + assertPred!"=="(Date(1999, 5, 1).endOfMonthDay, 31); + assertPred!"=="(Date(1999, 6, 1).endOfMonthDay, 30); + assertPred!"=="(Date(1999, 7, 1).endOfMonthDay, 31); + assertPred!"=="(Date(1999, 8, 1).endOfMonthDay, 31); + assertPred!"=="(Date(1999, 9, 1).endOfMonthDay, 30); + assertPred!"=="(Date(1999, 10, 1).endOfMonthDay, 31); + assertPred!"=="(Date(1999, 11, 1).endOfMonthDay, 30); + assertPred!"=="(Date(1999, 12, 1).endOfMonthDay, 31); + + //Test B.C. + assertPred!"=="(Date(-1999, 1, 1).endOfMonthDay, 31); + assertPred!"=="(Date(-1999, 2, 1).endOfMonthDay, 28); + assertPred!"=="(Date(-2000, 2, 1).endOfMonthDay, 29); + assertPred!"=="(Date(-1999, 3, 1).endOfMonthDay, 31); + assertPred!"=="(Date(-1999, 4, 1).endOfMonthDay, 30); + assertPred!"=="(Date(-1999, 5, 1).endOfMonthDay, 31); + assertPred!"=="(Date(-1999, 6, 1).endOfMonthDay, 30); + assertPred!"=="(Date(-1999, 7, 1).endOfMonthDay, 31); + assertPred!"=="(Date(-1999, 8, 1).endOfMonthDay, 31); + assertPred!"=="(Date(-1999, 9, 1).endOfMonthDay, 30); + assertPred!"=="(Date(-1999, 10, 1).endOfMonthDay, 31); + assertPred!"=="(Date(-1999, 11, 1).endOfMonthDay, 30); + assertPred!"=="(Date(-1999, 12, 1).endOfMonthDay, 31); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.endOfMonthDay = 30)); + static assert(!__traits(compiles, idate.endOfMonthDay = 30)); + + //Verify Examples. + assert(Date(1999, 1, 6).endOfMonthDay == 31); + assert(Date(1999, 2, 7).endOfMonthDay == 28); + assert(Date(2000, 2, 7).endOfMonthDay == 29); + assert(Date(2000, 6, 4).endOfMonthDay == 30); + } + + + /++ + Whether the current year is a date in A.D. + + Examples: +-------------------- +assert(Date(1, 1, 1).isAD); +assert(Date(2010, 12, 31).isAD); +assert(!Date(0, 12, 31).isAD); +assert(!Date(-2010, 1, 1).isAD); +-------------------- + +/ + @property bool isAD() const pure nothrow + { + return _year > 0; + } + + unittest + { + assert(Date(2010, 7, 4).isAD); + assert(Date(1, 1, 1).isAD); + assert(!Date(0, 1, 1).isAD); + assert(!Date(-1, 1, 1).isAD); + assert(!Date(-2010, 7, 4).isAD); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.isAD)); + static assert(__traits(compiles, idate.isAD)); + + //Verify Examples. + assert(Date(1, 1, 1).isAD); + assert(Date(2010, 12, 31).isAD); + assert(!Date(0, 12, 31).isAD); + assert(!Date(-2010, 1, 1).isAD); + } + + + /++ + The julian day for this Date at noon (since the julian day changes at noon). + +/ + @property long julianDay() const pure nothrow + { + return dayOfGregorianCal + 1_721_425; + } + + unittest + { + assertPred!"=="(Date(-4713, 11, 24).julianDay, 0); + assertPred!"=="(Date(0, 12, 31).julianDay, 1_721_425); + assertPred!"=="(Date(1, 1, 1).julianDay, 1_721_426); + assertPred!"=="(Date(1582, 10, 15).julianDay, 2_299_161); + assertPred!"=="(Date(1858, 11, 17).julianDay, 2_400_001); + assertPred!"=="(Date(1982, 1, 4).julianDay, 2_444_974); + assertPred!"=="(Date(1996, 3, 31).julianDay, 2_450_174); + assertPred!"=="(Date(2010, 8, 24).julianDay, 2_455_433); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.julianDay)); + static assert(__traits(compiles, idate.julianDay)); + } + + + /++ + The modified julian day for any time on this Date (since, the modified + julian day changes at midnight). + +/ + @property long modJulianDay() const pure nothrow + { + return julianDay - 2_400_001; + } + + unittest + { + assertPred!"=="(Date(1858, 11, 17).modJulianDay, 0); + assertPred!"=="(Date(2010, 8, 24).modJulianDay, 55_432); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.modJulianDay)); + static assert(__traits(compiles, idate.modJulianDay)); + } + + + /++ + Converts this Date to a string with the format YYYYMMDD. + + Examples: +-------------------- +assert(Date(2010, 7, 4).toISOString() == "20100704"); +assert(Date(1998, 12, 25).toISOString() == "19981225"); +assert(Date(0, 1, 5).toISOString() == "00000105"); +assert(Date(-4, 1, 5).toISOString() == "-00040105"); +-------------------- + +/ + string toISOString() const nothrow + { + try + { + if(_year >= 0) + { + if(_year < 10_000) + return format("%04d%02d%02d", _year, _month, _day); + else + return format("+%05d%02d%02d", _year, _month, _day); + } + else if(_year > -10_000) + return format("%05d%02d%02d", _year, _month, _day); + else + return format("%06d%02d%02d", _year, _month, _day); + } + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(9, 12, 4).toISOString(), "00091204"); + assertPred!"=="(Date(99, 12, 4).toISOString(), "00991204"); + assertPred!"=="(Date(999, 12, 4).toISOString(), "09991204"); + assertPred!"=="(Date(9999, 7, 4).toISOString(), "99990704"); + assertPred!"=="(Date(10000, 10, 20).toISOString(), "+100001020"); + + //Test B.C. + assertPred!"=="(Date(0, 12, 4).toISOString(), "00001204"); + assertPred!"=="(Date(-9, 12, 4).toISOString(), "-00091204"); + assertPred!"=="(Date(-99, 12, 4).toISOString(), "-00991204"); + assertPred!"=="(Date(-999, 12, 4).toISOString(), "-09991204"); + assertPred!"=="(Date(-9999, 7, 4).toISOString(), "-99990704"); + assertPred!"=="(Date(-10000, 10, 20).toISOString(), "-100001020"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.toISOString())); + static assert(__traits(compiles, idate.toISOString())); + + //Verify Examples. + assert(Date(2010, 7, 4).toISOString() == "20100704"); + assert(Date(1998, 12, 25).toISOString() == "19981225"); + assert(Date(0, 1, 5).toISOString() == "00000105"); + assert(Date(-4, 1, 5).toISOString() == "-00040105"); + } + + /++ + Converts this Date to a string with the format YYYY-MM-DD. + + Examples: +-------------------- +assert(Date(2010, 7, 4).toISOExtendedString() == "2010-07-04"); +assert(Date(1998, 12, 25).toISOExtendedString() == "1998-12-25"); +assert(Date(0, 1, 5).toISOExtendedString() == "0000-01-05"); +assert(Date(-4, 1, 5).toISOExtendedString() == "-0004-01-05"); +-------------------- + +/ + string toISOExtendedString() const nothrow + { + try + { + if(_year >= 0) + { + if(_year < 10_000) + return format("%04d-%02d-%02d", _year, _month, _day); + else + return format("+%05d-%02d-%02d", _year, _month, _day); + } + else if(_year > -10_000) + return format("%05d-%02d-%02d", _year, _month, _day); + else + return format("%06d-%02d-%02d", _year, _month, _day); + } + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(9, 12, 4).toISOExtendedString(), "0009-12-04"); + assertPred!"=="(Date(99, 12, 4).toISOExtendedString(), "0099-12-04"); + assertPred!"=="(Date(999, 12, 4).toISOExtendedString(), "0999-12-04"); + assertPred!"=="(Date(9999, 7, 4).toISOExtendedString(), "9999-07-04"); + assertPred!"=="(Date(10000, 10, 20).toISOExtendedString(), "+10000-10-20"); + + //Test B.C. + assertPred!"=="(Date(0, 12, 4).toISOExtendedString(), "0000-12-04"); + assertPred!"=="(Date(-9, 12, 4).toISOExtendedString(), "-0009-12-04"); + assertPred!"=="(Date(-99, 12, 4).toISOExtendedString(), "-0099-12-04"); + assertPred!"=="(Date(-999, 12, 4).toISOExtendedString(), "-0999-12-04"); + assertPred!"=="(Date(-9999, 7, 4).toISOExtendedString(), "-9999-07-04"); + assertPred!"=="(Date(-10000, 10, 20).toISOExtendedString(), "-10000-10-20"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.toISOExtendedString())); + static assert(__traits(compiles, idate.toISOExtendedString())); + + //Verify Examples. + assert(Date(2010, 7, 4).toISOExtendedString() == "2010-07-04"); + assert(Date(1998, 12, 25).toISOExtendedString() == "1998-12-25"); + assert(Date(0, 1, 5).toISOExtendedString() == "0000-01-05"); + assert(Date(-4, 1, 5).toISOExtendedString() == "-0004-01-05"); + } + + /++ + Converts this Date to a string with the format YYYY-Mon-DD. + + Examples: +-------------------- +assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); +assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); +assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); +assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); +-------------------- + +/ + string toSimpleString() const nothrow + { + try + { + if(_year >= 0) + { + if(_year < 10_000) + return format("%04d-%s-%02d", _year, monthToString(_month, false), _day); + else + return format("+%05d-%s-%02d", _year, monthToString(_month, false), _day); + } + else if(_year > -10_000) + return format("%05d-%s-%02d", _year, monthToString(_month, false), _day); + else + return format("%06d-%s-%02d", _year, monthToString(_month, false), _day); + } + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(Date(9, 12, 4).toSimpleString(), "0009-Dec-04"); + assertPred!"=="(Date(99, 12, 4).toSimpleString(), "0099-Dec-04"); + assertPred!"=="(Date(999, 12, 4).toSimpleString(), "0999-Dec-04"); + assertPred!"=="(Date(9999, 7, 4).toSimpleString(), "9999-Jul-04"); + assertPred!"=="(Date(10000, 10, 20).toSimpleString(), "+10000-Oct-20"); + + //Test B.C. + assertPred!"=="(Date(0, 12, 4).toSimpleString(), "0000-Dec-04"); + assertPred!"=="(Date(-9, 12, 4).toSimpleString(), "-0009-Dec-04"); + assertPred!"=="(Date(-99, 12, 4).toSimpleString(), "-0099-Dec-04"); + assertPred!"=="(Date(-999, 12, 4).toSimpleString(), "-0999-Dec-04"); + assertPred!"=="(Date(-9999, 7, 4).toSimpleString(), "-9999-Jul-04"); + assertPred!"=="(Date(-10000, 10, 20).toSimpleString(), "-10000-Oct-20"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.toSimpleString())); + static assert(__traits(compiles, idate.toSimpleString())); + + //Verify Examples. + assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); + assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); + assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); + assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); + } + + //TODO Add a function which returns a string in a user-specified format. + + + /+ + Converts this Date to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return toSimpleString(); + } + + /++ + Converts this Date to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return toSimpleString(); + } + + unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.toString())); + static assert(__traits(compiles, cdate.toString())); + static assert(__traits(compiles, idate.toString())); + } + + + /++ + Creates a Date from a string with the format YYYYMMDD. Whitespace is + stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates. + + Throws: + DateTimeException if the given string is not in the ISO format or if + the resulting Date would not be valid. + + Examples: +-------------------- +assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); +assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); +assert(Date.fromISOString("00000105") == Date(0, 1, 5)); +assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); +assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); +-------------------- + +/ + static Date fromISOString(S)(in S isoString) + if(isSomeString!S) + { + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 8, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto day = dstr[$-2 .. $]; + auto month = dstr[$-4 .. $-2]; + auto year = dstr[0 .. $-4]; + + enforce(!canFind!((dchar c){return !isdigit(c);})(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + if(year.length > 4) + { + enforce(year.startsWith("-") || year.startsWith("+"), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(year[1..$]), new DateTimeException(format("Invalid ISO String: %s", isoString))); + } + else + enforce(!canFind!((dchar c){return !isdigit(c);})(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + } + + unittest + { + assertThrown!DateTimeException(Date.fromISOString("")); + assertThrown!DateTimeException(Date.fromISOString("990704")); + assertThrown!DateTimeException(Date.fromISOString("0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070")); + assertThrown!DateTimeException(Date.fromISOString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOString("120100704")); + assertThrown!DateTimeException(Date.fromISOString("-0100704")); + assertThrown!DateTimeException(Date.fromISOString("+0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070a")); + assertThrown!DateTimeException(Date.fromISOString("20100a04")); + assertThrown!DateTimeException(Date.fromISOString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); + + assertPred!"=="(Date.fromISOString("19990706"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOString("-19990706"), Date(-1999, 7, 6)); + assertPred!"=="(Date.fromISOString("+019990706"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOString("19990706 "), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOString(" 19990706"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOString(" 19990706 "), Date(1999, 7, 6)); + + //Verify Examples. + assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); + assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); + assert(Date.fromISOString("00000105") == Date(0, 1, 5)); + assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); + assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); + } + + + /++ + Creates a Date from a string with the format YYYY-MM-DD. Whitespace is + stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for + dates. + + Throws: + DateTimeException if the given string is not in the ISO Extended + format or if the resulting Date would not be valid. + + Examples: +-------------------- +assert(Date.fromISOExtendedString("2010-07-04") == Date(2010, 7, 4)); +assert(Date.fromISOExtendedString("1998-12-25") == Date(1998, 12, 25)); +assert(Date.fromISOExtendedString("0000-01-05") == Date(0, 1, 5)); +assert(Date.fromISOExtendedString("-0004-01-05") == Date(-4, 1, 5)); +assert(Date.fromISOExtendedString(" 2010-07-04 ") == Date(2010, 7, 4)); +-------------------- + +/ + static Date fromISOExtendedString(S)(in S isoExtString) + if(isSomeString!(S)) + { + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto day = dstr[$-2 .. $]; + auto month = dstr[$-5 .. $-3]; + auto year = dstr[0 .. $-6]; + + enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(day), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(month), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + if(year.length > 4) + { + enforce(year.startsWith("-") || year.startsWith("+"), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(year[1..$]), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + } + else + enforce(!canFind!((dchar c){return !isdigit(c);})(year), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + } + + unittest + { + assertThrown!DateTimeException(Date.fromISOExtendedString("")); + assertThrown!DateTimeException(Date.fromISOExtendedString("990704")); + assertThrown!DateTimeException(Date.fromISOExtendedString("0100704")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010070")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOExtendedString("120100704")); + assertThrown!DateTimeException(Date.fromISOExtendedString("-0100704")); + assertThrown!DateTimeException(Date.fromISOExtendedString("+0100704")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010070a")); + assertThrown!DateTimeException(Date.fromISOExtendedString("20100a04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOExtendedString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOExtendedString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOExtendedString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOExtendedString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOExtendedString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOExtendedString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOExtendedString("20100704")); + assertThrown!DateTimeException(Date.fromISOExtendedString("2010-Jul-04")); + + assertPred!"=="(Date.fromISOExtendedString("1999-07-06"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOExtendedString("-1999-07-06"), Date(-1999, 7, 6)); + assertPred!"=="(Date.fromISOExtendedString("+01999-07-06"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOExtendedString("1999-07-06 "), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOExtendedString(" 1999-07-06"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromISOExtendedString(" 1999-07-06 "), Date(1999, 7, 6)); + + //Verify Examples. + assert(Date.fromISOExtendedString("2010-07-04") == Date(2010, 7, 4)); + assert(Date.fromISOExtendedString("1998-12-25") == Date(1998, 12, 25)); + assert(Date.fromISOExtendedString("0000-01-05") == Date(0, 1, 5)); + assert(Date.fromISOExtendedString("-0004-01-05") == Date(-4, 1, 5)); + assert(Date.fromISOExtendedString(" 2010-07-04 ") == Date(2010, 7, 4)); + } + + + /++ + Creates a Date from a string with the format YYYY-Mon-DD. Whitespace is + stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString() + formats dates. + + Throws: + DateTimeException if the given string is not in the correct format or + if the resulting Date would not be valid. + + Examples: +-------------------- +assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); +assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); +assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); +assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); +assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); +-------------------- + +/ + static Date fromSimpleString(S)(in S simpleString) + if(isSomeString!(S)) + { + auto dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString))); + + auto day = dstr[$-2 .. $]; + auto month = monthFromString(to!string(dstr[$-6 .. $-3])); + auto year = dstr[0 .. $-7]; + + enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(day), new DateTimeException(format("Invalid string format: %s", simpleString))); + + if(year.length > 4) + { + enforce(year.startsWith("-") || year.startsWith("+"), new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(year[1..$]), new DateTimeException(format("Invalid string format: %s", simpleString))); + } + else + enforce(!canFind!((dchar c){return !isdigit(c);})(year), new DateTimeException(format("Invalid string format: %s", simpleString))); + + return Date(to!short(year), month, to!ubyte(day)); + } + + unittest + { + assertThrown!DateTimeException(Date.fromSimpleString("")); + assertThrown!DateTimeException(Date.fromSimpleString("990704")); + assertThrown!DateTimeException(Date.fromSimpleString("0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); + assertThrown!DateTimeException(Date.fromSimpleString("120100704")); + assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); + assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromSimpleString("20100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); + + assertPred!"=="(Date.fromSimpleString("1999-Jul-06"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromSimpleString("-1999-Jul-06"), Date(-1999, 7, 6)); + assertPred!"=="(Date.fromSimpleString("+01999-Jul-06"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromSimpleString("1999-Jul-06 "), Date(1999, 7, 6)); + assertPred!"=="(Date.fromSimpleString(" 1999-Jul-06"), Date(1999, 7, 6)); + assertPred!"=="(Date.fromSimpleString(" 1999-Jul-06 "), Date(1999, 7, 6)); + + //Verify Examples. + assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); + assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); + assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); + assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); + assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); + } + + + //TODO Add function which takes a user-specified time format and produces a Date + + //TODO Add function which takes pretty much any time-string and produces a Date + // Obviously, it will be less efficient, and it probably won't manage _every_ + // possible date format, but a smart conversion function would be nice. + + + /++ + Returns the Date farthest in the past which is representable by Date. + +/ + @property static Date min() pure nothrow + { + auto date = Date.init; + date._year = short.min; + date._month = Month.jan; + date._day = 1; + + return date; + } + + unittest + { + assert(Date.min.year < 0); + assert(Date.min < Date.max); + } + + + /++ + Returns the Date farthest in the future which is representable by Date. + +/ + @property static Date max() pure nothrow + { + auto date = Date.init; + date._year = short.max; + date._month = Month.dec; + date._day = 31; + + return date; + } + + unittest + { + assert(Date.max.year > 0); + assert(Date.max > Date.min); + } + + +private: + + /++ + Whether the given values form a valid date. + + Params: + year = The year to test. + month = The month of the Gregorian Calendar to test. + day = The day of the month to test. + +/ + static bool _valid(int year, int month, int day) pure nothrow + { + if(!valid!"months"(month)) + return false; + + return valid!"days"(year, month, day); + } + + /++ + Adds the given number of days to this Date. A negative number will subtract. + + The month will be adjusted along with the day if the number of days added + (or subtracted) would overflow (or underflow) the current month. The year + will be adjusted along with the month if the increase (or decrease) to the + month would cause it to overflow (or underflow) the current year. + + addDays(numDays) is effectively equivalent to date.dayOfGregorianCal = date.dayOfGregorianCal + days. + + Params: + days = The number of days to add to this Date. + +/ + ref Date addDays(long days) pure nothrow + { + dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); + + return this; + } + + unittest + { + //Test A.D. + { + auto date = Date(1999, 2, 28); + date.addDays(1); + assertPred!"=="(date, Date(1999, 3, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 28); + date.addDays(1); + assertPred!"=="(date, Date(2000, 2, 29)); + date.addDays(1); + assertPred!"=="(date, Date(2000, 3, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 6, 30); + date.addDays(1); + assertPred!"=="(date, Date(1999, 7, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 7, 31); + date.addDays(1); + assertPred!"=="(date, Date(1999, 8, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 1, 1); + date.addDays(-1); + assertPred!"=="(date, Date(1998, 12, 31)); + date.addDays(1); + assertPred!"=="(date, Date(1999, 1, 1)); + } + + { + auto date = Date(1999, 7, 6); + date.addDays(9); + assertPred!"=="(date, Date(1999, 7, 15)); + date.addDays(-11); + assertPred!"=="(date, Date(1999, 7, 4)); + date.addDays(30); + assertPred!"=="(date, Date(1999, 8, 3)); + date.addDays(-3); + assertPred!"=="(date, Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 7, 6); + date.addDays(365); + assertPred!"=="(date, Date(2000, 7, 5)); + date.addDays(-365); + assertPred!"=="(date, Date(1999, 7, 6)); + date.addDays(366); + assertPred!"=="(date, Date(2000, 7, 6)); + date.addDays(730); + assertPred!"=="(date, Date(2002, 7, 6)); + date.addDays(-1096); + assertPred!"=="(date, Date(1999, 7, 6)); + } + + //Test B.C. + { + auto date = Date(-1999, 2, 28); + date.addDays(1); + assertPred!"=="(date, Date(-1999, 3, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 28); + date.addDays(1); + assertPred!"=="(date, Date(-2000, 2, 29)); + date.addDays(1); + assertPred!"=="(date, Date(-2000, 3, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 6, 30); + date.addDays(1); + assertPred!"=="(date, Date(-1999, 7, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 7, 31); + date.addDays(1); + assertPred!"=="(date, Date(-1999, 8, 1)); + date.addDays(-1); + assertPred!"=="(date, Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 1, 1); + date.addDays(-1); + assertPred!"=="(date, Date(-2000, 12, 31)); + date.addDays(1); + assertPred!"=="(date, Date(-1999, 1, 1)); + } + + { + auto date = Date(-1999, 7, 6); + date.addDays(9); + assertPred!"=="(date, Date(-1999, 7, 15)); + date.addDays(-11); + assertPred!"=="(date, Date(-1999, 7, 4)); + date.addDays(30); + assertPred!"=="(date, Date(-1999, 8, 3)); + date.addDays(-3); + } + + { + auto date = Date(-1999, 7, 6); + date.addDays(365); + assertPred!"=="(date, Date(-1998, 7, 6)); + date.addDays(-365); + assertPred!"=="(date, Date(-1999, 7, 6)); + date.addDays(366); + assertPred!"=="(date, Date(-1998, 7, 7)); + date.addDays(730); + assertPred!"=="(date, Date(-1996, 7, 6)); + date.addDays(-1096); + assertPred!"=="(date, Date(-1999, 7, 6)); + } + + //Test Both + { + auto date = Date(1, 7, 6); + date.addDays(-365); + assertPred!"=="(date, Date(0, 7, 6)); + date.addDays(365); + assertPred!"=="(date, Date(1, 7, 6)); + date.addDays(-731); + assertPred!"=="(date, Date(-1, 7, 6)); + date.addDays(730); + assertPred!"=="(date, Date(1, 7, 5)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.addDays(12))); + static assert(!__traits(compiles, idate.addDays(12))); + } + + + pure invariant() + { + assert(valid!"months"(_month), "Invariant Failure: year [" ~ + numToString(_year) ~ + "] month [" ~ + numToString(_month) ~ + "] day [" ~ + numToString(_day) ~ + "]"); + assert(valid!"days"(_year, _month, _day), "Invariant Failure: year [" ~ + numToString(_year) ~ + "] month [" ~ + numToString(_month) ~ + "] day [" ~ + numToString(_day) ~ + "]"); + } + + + short _year = 1; + Month _month = Month.jan; + ubyte _day = 1; +} + + + +/++ + Represents a time of day with hours, minutes, and seconds. It uses 24 hour time. ++/ +struct TimeOfDay +{ +public: + + /++ + Params: + hour = Hour of the day [0 - 24$(RPAREN). + minute = Minute of the hour [0 - 60$(RPAREN). + second = Second of the minute [0 - 60$(RPAREN). + + Throws: + DateTimeException if the resulting TimeOfDay would be not be valid. + +/ + this(int hour, int minute, int second = 0) pure + { + enforceValid!"hours"(hour); + enforceValid!"minutes"(minute); + enforceValid!"seconds"(second); + + _hour = cast(ubyte)hour; + _minute = cast(ubyte)minute; + _second = cast(ubyte)second; + } + + unittest + { + assert(TimeOfDay(0, 0) == TimeOfDay.init); + + { + auto tod = TimeOfDay(0, 0); + assertPred!"=="(tod._hour, 0); + assertPred!"=="(tod._minute, 0); + assertPred!"=="(tod._second, 0); + } + + { + auto tod = TimeOfDay(12, 30, 33); + assertPred!"=="(tod._hour, 12); + assertPred!"=="(tod._minute, 30); + assertPred!"=="(tod._second, 33); + } + + { + auto tod = TimeOfDay(23, 59, 59); + assertPred!"=="(tod._hour, 23); + assertPred!"=="(tod._minute, 59); + assertPred!"=="(tod._second, 59); + } + + assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); + } + + + /++ + Compares this TimeOfDay with the given TimeOfDay. + + Returns: + $(TABLE + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in TimeOfDay rhs) const pure nothrow + { + if(_hour < rhs._hour) + return -1; + if(_hour > rhs._hour) + return 1; + + if(_minute < rhs._minute) + return -1; + if(_minute > rhs._minute) + return 1; + + if(_second < rhs._second) + return -1; + if(_second > rhs._second) + return 1; + + return 0; + } + + unittest + { + assertPred!("opCmp", "==")(TimeOfDay(0, 0, 0), TimeOfDay.init); + + assertPred!("opCmp", "==")(TimeOfDay(0, 0, 0), TimeOfDay(0, 0, 0)); + assertPred!("opCmp", "==")(TimeOfDay(12, 0, 0), TimeOfDay(12, 0, 0)); + assertPred!("opCmp", "==")(TimeOfDay(0, 30, 0), TimeOfDay(0, 30, 0)); + assertPred!("opCmp", "==")(TimeOfDay(0, 0, 33), TimeOfDay(0, 0, 33)); + + assertPred!("opCmp", "==")(TimeOfDay(12, 30, 0), TimeOfDay(12, 30, 0)); + assertPred!("opCmp", "==")(TimeOfDay(12, 30, 33), TimeOfDay(12, 30, 33)); + + assertPred!("opCmp", "==")(TimeOfDay(0, 30, 33), TimeOfDay(0, 30, 33)); + assertPred!("opCmp", "==")(TimeOfDay(0, 0, 33), TimeOfDay(0, 0, 33)); + + assertPred!("opCmp", "<")(TimeOfDay(12, 30, 33), TimeOfDay(13, 30, 33)); + assertPred!("opCmp", ">")(TimeOfDay(13, 30, 33), TimeOfDay(12, 30, 33)); + assertPred!("opCmp", "<")(TimeOfDay(12, 30, 33), TimeOfDay(12, 31, 33)); + assertPred!("opCmp", ">")(TimeOfDay(12, 31, 33), TimeOfDay(12, 30, 33)); + assertPred!("opCmp", "<")(TimeOfDay(12, 30, 33), TimeOfDay(12, 30, 34)); + assertPred!("opCmp", ">")(TimeOfDay(12, 30, 34), TimeOfDay(12, 30, 33)); + + assertPred!("opCmp", ">")(TimeOfDay(13, 30, 33), TimeOfDay(12, 30, 34)); + assertPred!("opCmp", "<")(TimeOfDay(12, 30, 34), TimeOfDay(13, 30, 33)); + assertPred!("opCmp", ">")(TimeOfDay(13, 30, 33), TimeOfDay(12, 31, 33)); + assertPred!("opCmp", "<")(TimeOfDay(12, 31, 33), TimeOfDay(13, 30, 33)); + + assertPred!("opCmp", ">")(TimeOfDay(12, 31, 33), TimeOfDay(12, 30, 34)); + assertPred!("opCmp", "<")(TimeOfDay(12, 30, 34), TimeOfDay(12, 31, 33)); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, ctod.opCmp(itod))); + static assert(__traits(compiles, itod.opCmp(ctod))); + } + + + /++ + Hours passed midnight. + +/ + @property ubyte hour() const pure nothrow + { + return _hour; + } + + unittest + { + assertPred!"=="(TimeOfDay.init.hour, 0); + assertPred!"=="(TimeOfDay(12, 0, 0).hour, 12); + + const ctod = TimeOfDay(12, 0, 0); + immutable itod = TimeOfDay(12, 0, 0); + static assert(__traits(compiles, ctod.hour == 12)); + static assert(__traits(compiles, itod.hour == 12)); + } + + + /++ + Hours passed midnight. + + Params: + hour = The hour of the day to set this TimeOfDay's hour to. + + Throws: + DateTimeException if the given hour would result in an invalid TimeOfDay. + +/ + @property void hour(int hour) pure + { + enforceValid!"hours"(hour); + _hour = cast(ubyte)hour; + } + + unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.hour = 12; + assertPred!"=="(tod, TimeOfDay(12, 0, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.hour = 12)); + static assert(!__traits(compiles, itod.hour = 12)); + } + + + /++ + Minutes passed the hour. + +/ + @property ubyte minute() const pure nothrow + { + return _minute; + } + + unittest + { + assertPred!"=="(TimeOfDay.init.minute, 0); + assertPred!"=="(TimeOfDay(0, 30, 0).minute, 30); + + const ctod = TimeOfDay(0, 30, 0); + immutable itod = TimeOfDay(0, 30, 0); + static assert(__traits(compiles, ctod.minute == 30)); + static assert(__traits(compiles, itod.minute == 30)); + } + + + /++ + Minutes passed the hour. + + Params: + minute = The minute to set this TimeOfDay's minute to. + + Throws: + DateTimeException if the given minute would result in an invalid TimeOfDay. + +/ + @property void minute(int minute) pure + { + enforceValid!"minutes"(minute); + _minute = cast(ubyte)minute; + } + + unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.minute = 30; + assertPred!"=="(tod, TimeOfDay(0, 30, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.minute = 30)); + static assert(!__traits(compiles, itod.minute = 30)); + } + + + /++ + Seconds passed the minute. + +/ + @property ubyte second() const pure nothrow + { + return _second; + } + + unittest + { + assertPred!"=="(TimeOfDay.init.second, 0); + assertPred!"=="(TimeOfDay(0, 0, 33).second, 33); + + const ctod = TimeOfDay(0, 0, 33); + immutable itod = TimeOfDay(0, 0, 33); + static assert(__traits(compiles, ctod.second == 33)); + static assert(__traits(compiles, itod.second == 33)); + } + + + /++ + Seconds passed the minute. + + Params: + second = The second to set this TimeOfDay's second to. + + Throws: + DateTimeException if the given second would result in an invalid TimeOfDay. + +/ + @property void second(int second) pure + { + enforceValid!"seconds"(second); + _second = cast(ubyte)second; + } + + unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.second = 33; + assertPred!"=="(tod, TimeOfDay(0, 0, 33)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.second = 33)); + static assert(!__traits(compiles, itod.second = 33)); + } + + + /++ + Adds the given number of hours to this TimeOfDay. A negative number will + subtract. + + For hours, because they are the largest unit in TimeOfDay, there is no + difference between adding or rolling. + + Note that TimeOfDay has no $(D add!()) function because you can add to a + TimeOfDay by adding a duration to it. + + Params: + hours = The number of hours to add to this TimeOfDay. + + Examples: +-------------------- +auto tod1 = TimeOfDay(7, 12, 0); +tod1.roll!"hours"(1); +assert(tod1 == TimeOfDay(8, 12, 0)); + +auto tod2 = TimeOfDay(7, 12, 0); +tod2.roll!"hours"(-1); +assert(tod2 == TimeOfDay(6, 12, 0)); + +auto tod3 = TimeOfDay(23, 0, 0); +tod3.roll!"hours"(1); +assert(tod3 == TimeOfDay(0, 0, 0)); + +auto tod4 = TimeOfDay(0, 0, 0); +tod4.roll!"hours"(-1); +assert(tod4 == TimeOfDay(23, 0, 0)); +-------------------- + +/ + /+ref TimeOfDay+/ void roll(string units)(long hours) pure nothrow + if(units == "hours") + { + this += dur!"hours"(hours); + } + + unittest + { + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"hours"(53))); + static assert(!__traits(compiles, itod.roll!"hours"(53))); + + //Verify Examples. + auto tod1 = TimeOfDay(7, 12, 0); + tod1.roll!"hours"(1); + assert(tod1 == TimeOfDay(8, 12, 0)); + + auto tod2 = TimeOfDay(7, 12, 0); + tod2.roll!"hours"(-1); + assert(tod2 == TimeOfDay(6, 12, 0)); + + auto tod3 = TimeOfDay(23, 0, 0); + tod3.roll!"hours"(1); + assert(tod3 == TimeOfDay(0, 0, 0)); + + auto tod4 = TimeOfDay(0, 0, 0); + tod4.roll!"hours"(-1); + assert(tod4 == TimeOfDay(23, 0, 0)); + } + + + /++ + Add minutes to the time of day. Negative values will subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the TimeOfDay 60 minutes, you get the exact + same TimeOfDay. + + Note that TimeOfDay has no $(D add!()) function because you can add to a + TimeOfDay by adding a duration to it. + + Params: + minutes = The number of Minutes to add to this TimeOfDay. + + Examples: +-------------------- +auto tod1 = TimeOfDay(7, 12, 0); +tod1.roll!"minutes"(1); +assert(tod1 == TimeOfDay(7, 13, 0)); + +auto tod2 = TimeOfDay(7, 12, 0); +tod2.roll!"minutes"(-1); +assert(tod2 == TimeOfDay(7, 11, 0)); + +auto tod3 = TimeOfDay(23, 59, 0); +tod3.roll!"minutes"(1); +assert(tod3 == TimeOfDay(23, 0, 0)); + +auto tod4 = TimeOfDay(0, 0, 0); +tod4.roll!"minutes"(-1); +assert(tod4 == TimeOfDay(0, 59, 0)); + +auto tod5 = TimeOfDay(7, 32, 12); +tod5.roll!"seconds"(1); +assert(tod5 == TimeOfDay(7, 32, 13)); + +auto tod6 = TimeOfDay(7, 32, 12); +tod6.roll!"seconds"(-1); +assert(tod6 == TimeOfDay(7, 32, 11)); + +auto tod7 = TimeOfDay(23, 59, 59); +tod7.roll!"seconds"(1); +assert(tod7 == TimeOfDay(23, 59, 0)); + +auto tod8 = TimeOfDay(0, 0, 0); +tod8.roll!"seconds"(-1); +assert(tod8 == TimeOfDay(0, 0, 59)); +-------------------- + +/ + /+ref TimeOfDay+/ void roll(string units)(long value) pure nothrow + if(units == "minutes" || + units == "seconds") + { + static if(units == "minutes") + enum memberVarStr = "minute"; + else static if(units == "seconds") + enum memberVarStr = "second"; + else + static assert(0); + + value %= 60; + mixin("auto newVal = cast(ubyte)(_" ~ memberVarStr ~ ") + value;"); + + if(value < 0) + { + if(newVal < 0) + newVal += 60; + } + else if(newVal >= 60) + newVal -= 60; + + mixin("_" ~ memberVarStr ~ " = cast(ubyte)newVal;"); + } + + //Test roll!"minutes"(). + unittest + { + static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); + + testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); + + testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); + + testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); + testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); + testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); + + testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); + testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); + testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); + + testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); + testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); + testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"minutes"(7))); + static assert(!__traits(compiles, itod.roll!"minutes"(7))); + + //Verify Examples. + auto tod1 = TimeOfDay(7, 12, 0); + tod1.roll!"minutes"(1); + assert(tod1 == TimeOfDay(7, 13, 0)); + + auto tod2 = TimeOfDay(7, 12, 0); + tod2.roll!"minutes"(-1); + assert(tod2 == TimeOfDay(7, 11, 0)); + + auto tod3 = TimeOfDay(23, 59, 0); + tod3.roll!"minutes"(1); + assert(tod3 == TimeOfDay(23, 0, 0)); + + auto tod4 = TimeOfDay(0, 0, 0); + tod4.roll!"minutes"(-1); + assert(tod4 == TimeOfDay(0, 59, 0)); + + auto tod5 = TimeOfDay(7, 32, 12); + tod5.roll!"seconds"(1); + assert(tod5 == TimeOfDay(7, 32, 13)); + + auto tod6 = TimeOfDay(7, 32, 12); + tod6.roll!"seconds"(-1); + assert(tod6 == TimeOfDay(7, 32, 11)); + + auto tod7 = TimeOfDay(23, 59, 59); + tod7.roll!"seconds"(1); + assert(tod7 == TimeOfDay(23, 59, 0)); + + auto tod8 = TimeOfDay(0, 0, 0); + tod8.roll!"seconds"(-1); + assert(tod8 == TimeOfDay(0, 0, 59)); + } + + //Test roll!"seconds"(). + unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"seconds"(7))); + static assert(!__traits(compiles, itod.roll!"seconds"(7))); + } + + + /++ + Gives the result of adding or subtracting a duration from this TimeOfDay. + + The legal types of arithmetic for TimeOfDay using this operator are + + $(TABLE + $(TR $(TD TimeOfDay) $(TD +) $(TD duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The duration to add to or subtract from this Date. + +/ + TimeOfDay opBinary(string op, D)(in D duration) const pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + TimeOfDay retval = this; + + static if(is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if(is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + //Ideally, this would just be + //return retval.addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedHNSecs = hnsecs; + else static if(op == "-") + immutable signedHNSecs = -hnsecs; + else + static assert(0); + + return retval.addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + } + + unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assertPred!"=="(tod + dur!"hours"(7), TimeOfDay(19, 30, 33)); + assertPred!"=="(tod + dur!"hours"(-7), TimeOfDay(5, 30, 33)); + assertPred!"=="(tod + dur!"minutes"(7), TimeOfDay(12, 37, 33)); + assertPred!"=="(tod + dur!"minutes"(-7), TimeOfDay(12, 23, 33)); + assertPred!"=="(tod + dur!"seconds"(7), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod + dur!"seconds"(-7), TimeOfDay(12, 30, 26)); + + assertPred!"=="(tod + dur!"msecs"(7000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod + dur!"msecs"(-7000), TimeOfDay(12, 30, 26)); + assertPred!"=="(tod + dur!"usecs"(7_000_000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod + dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 26)); + assertPred!"=="(tod + dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod + dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 26)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(tod + TickDuration.from!"usecs"(7_000_000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod + TickDuration.from!"usecs"(-7_000_000), TimeOfDay(12, 30, 26)); + } + + assertPred!"=="(tod - dur!"hours"(-7), TimeOfDay(19, 30, 33)); + assertPred!"=="(tod - dur!"hours"(7), TimeOfDay(5, 30, 33)); + assertPred!"=="(tod - dur!"minutes"(-7), TimeOfDay(12, 37, 33)); + assertPred!"=="(tod - dur!"minutes"(7), TimeOfDay(12, 23, 33)); + assertPred!"=="(tod - dur!"seconds"(-7), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod - dur!"seconds"(7), TimeOfDay(12, 30, 26)); + + assertPred!"=="(tod - dur!"msecs"(-7000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod - dur!"msecs"(7000), TimeOfDay(12, 30, 26)); + assertPred!"=="(tod - dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod - dur!"usecs"(7_000_000), TimeOfDay(12, 30, 26)); + assertPred!"=="(tod - dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod - dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 26)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(tod - TickDuration.from!"usecs"(-7_000_000), TimeOfDay(12, 30, 40)); + assertPred!"=="(tod - TickDuration.from!"usecs"(7_000_000), TimeOfDay(12, 30, 26)); + } + + auto duration = dur!"hours"(11); + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(__traits(compiles, tod + duration)); + static assert(__traits(compiles, ctod + duration)); + static assert(__traits(compiles, itod + duration)); + + static assert(__traits(compiles, tod - duration)); + static assert(__traits(compiles, ctod - duration)); + static assert(__traits(compiles, itod - duration)); + } + + + /++ + Gives the result of adding or subtracting a duration from this TimeOfDay, + as well as assigning the result to this TimeOfDay. + + The legal types of arithmetic for TimeOfDay using this operator are + + $(TABLE + $(TR $(TD TimeOfDay) $(TD +) $(TD duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The duration to add to or subtract from this TimeOfDay. + +/ + /+ref+/ TimeOfDay opOpAssign(string op, D)(in D duration) pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + static if(is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if(is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + //Ideally, this would just be + //return addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedHNSecs = hnsecs; + else static if(op == "-") + immutable signedHNSecs = -hnsecs; + else + static assert(0); + + return addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + } + + unittest + { + auto duration = dur!"hours"(12); + + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hours"(7), TimeOfDay(19, 30, 33)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hours"(-7), TimeOfDay(5, 30, 33)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"minutes"(7), TimeOfDay(12, 37, 33)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"minutes"(-7), TimeOfDay(12, 23, 33)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"seconds"(7), TimeOfDay(12, 30, 40)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"seconds"(-7), TimeOfDay(12, 30, 26)); + + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"msecs"(7000), TimeOfDay(12, 30, 40)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"msecs"(-7000), TimeOfDay(12, 30, 26)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"usecs"(7_000_000), TimeOfDay(12, 30, 40)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 26)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 40)); + assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 26)); + + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hours"(-7), TimeOfDay(19, 30, 33)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hours"(7), TimeOfDay(5, 30, 33)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"minutes"(-7), TimeOfDay(12, 37, 33)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"minutes"(7), TimeOfDay(12, 23, 33)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"seconds"(-7), TimeOfDay(12, 30, 40)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"seconds"(7), TimeOfDay(12, 30, 26)); + + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"msecs"(-7000), TimeOfDay(12, 30, 40)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"msecs"(7000), TimeOfDay(12, 30, 26)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 40)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"usecs"(7_000_000), TimeOfDay(12, 30, 26)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 40)); + assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 26)); + + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(!__traits(compiles, ctod += duration)); + static assert(!__traits(compiles, itod += duration)); + static assert(!__traits(compiles, ctod -= duration)); + static assert(!__traits(compiles, itod -= duration)); + } + + + /++ + Gives the difference between two TimeOfDays. + + The legal types of arithmetic for TimeOfDay using this operator are + + $(TABLE + $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) + ) + + Params: + rhs = The TimeOfDay to subtract from this one. + +/ + Duration opBinary(string op)(in TimeOfDay rhs) const pure nothrow + if(op == "-") + { + immutable lhsSec = _hour * 3600 + _minute * 60 + _second; + immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; + + return dur!"seconds"(lhsSec - rhsSec); + } + + unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assertPred!"=="(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33), dur!"seconds"(-19_061)); + assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52), dur!"seconds"(19_061)); + assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33), dur!"seconds"(-7200)); + assertPred!"=="(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33), dur!"seconds"(7200)); + assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33), dur!"seconds"(-240)); + assertPred!"=="(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33), dur!"seconds"(240)); + assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34), dur!"seconds"(-1)); + assertPred!"=="(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33), dur!"seconds"(1)); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod - tod)); + static assert(__traits(compiles, ctod - tod)); + static assert(__traits(compiles, itod - tod)); + + static assert(__traits(compiles, tod - ctod)); + static assert(__traits(compiles, ctod - ctod)); + static assert(__traits(compiles, itod - ctod)); + + static assert(__traits(compiles, tod - itod)); + static assert(__traits(compiles, ctod - itod)); + static assert(__traits(compiles, itod - itod)); + } + + + /++ + Converts this TimeOfDay to a string with the format HHMMSS. + + Examples: +-------------------- +assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); +assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); +-------------------- + +/ + string toISOString() const nothrow + { + try + return format("%02d%02d%02d", _hour, _minute, _second); + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod.toISOString())); + static assert(__traits(compiles, ctod.toISOString())); + static assert(__traits(compiles, itod.toISOString())); + + //Verify Examples. + assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); + assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); + } + + + /++ + Converts this TimeOfDay to a string with the format HH:MM:SS. + + Examples: +-------------------- +assert(TimeOfDay(0, 0, 0).toISOExtendedString() == "000000"); +assert(TimeOfDay(12, 30, 33).toISOExtendedString() == "123033"); +-------------------- + +/ + string toISOExtendedString() const nothrow + { + try + return format("%02d:%02d:%02d", _hour, _minute, _second); + catch(Exception e) + assert(0, "format() threw."); + } + + + unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod.toISOExtendedString())); + static assert(__traits(compiles, ctod.toISOExtendedString())); + static assert(__traits(compiles, itod.toISOExtendedString())); + + //Verify Examples. + assert(TimeOfDay(0, 0, 0).toISOExtendedString() == "00:00:00"); + assert(TimeOfDay(12, 30, 33).toISOExtendedString() == "12:30:33"); + } + + + /+ + Converts this TimeOfDay to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return toISOExtendedString(); + } + + + /++ + Converts this TimeOfDay to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return toISOExtendedString(); + } + + unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod.toString())); + static assert(__traits(compiles, ctod.toString())); + static assert(__traits(compiles, itod.toString())); + } + + //TODO Add a function which returns a string in a user-specified format. + + + + /++ + Creates a TimeOfDay from a string with the format HHMMSS. Whitespace is + stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for times. + + Throws: + DateTimeException if the given string is not in the ISO format or if + the resulting TimeOfDay would not be valid. + + Examples: +-------------------- +assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); +assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); +assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); +-------------------- + +/ + static TimeOfDay fromISOString(S)(in S isoString) + if(isSomeString!S) + { + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[2 .. 4]; + auto seconds = dstr[4 .. $]; + + enforce(!canFind!((dchar c){return !isdigit(c);})(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); + + assertPred!"=="(TimeOfDay.fromISOString("011217"), TimeOfDay(1, 12, 17)); + assertPred!"=="(TimeOfDay.fromISOString("001412"), TimeOfDay(0, 14, 12)); + assertPred!"=="(TimeOfDay.fromISOString("000007"), TimeOfDay(0, 0, 7)); + assertPred!"=="(TimeOfDay.fromISOString("011217 "), TimeOfDay(1, 12, 17)); + assertPred!"=="(TimeOfDay.fromISOString(" 011217"), TimeOfDay(1, 12, 17)); + assertPred!"=="(TimeOfDay.fromISOString(" 011217 "), TimeOfDay(1, 12, 17)); + + //Verify Examples. + assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); + } + + + /++ + Creates a TimeOfDay from a string with the format HH:MM:SS. Whitespace is + stripped from the given string. + + Params: + isoString = A string formatted in the ISO Extended format for times. + + Throws: + DateTimeException if the given string is not in the ISO Extended + format or if the resulting TimeOfDay would not be valid. + + Examples: +-------------------- +assert(TimeOfDay.fromISOExtendedString("00:00:00") == TimeOfDay(0, 0, 0)); +assert(TimeOfDay.fromISOExtendedString("12:30:33") == TimeOfDay(12, 30, 33)); +assert(TimeOfDay.fromISOExtendedString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); +-------------------- + +/ + static TimeOfDay fromISOExtendedString(S)(in S isoExtString) + if(isSomeString!S) + { + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[3 .. 5]; + auto seconds = dstr[6 .. $]; + + enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(hours), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(minutes), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(!canFind!((dchar c){return !isdigit(c);})(seconds), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtendedString("120033")); + + assertPred!"=="(TimeOfDay.fromISOExtendedString("01:12:17"), TimeOfDay(1, 12, 17)); + assertPred!"=="(TimeOfDay.fromISOExtendedString("00:14:12"), TimeOfDay(0, 14, 12)); + assertPred!"=="(TimeOfDay.fromISOExtendedString("00:00:07"), TimeOfDay(0, 0, 7)); + assertPred!"=="(TimeOfDay.fromISOExtendedString("01:12:17 "), TimeOfDay(1, 12, 17)); + assertPred!"=="(TimeOfDay.fromISOExtendedString(" 01:12:17"), TimeOfDay(1, 12, 17)); + assertPred!"=="(TimeOfDay.fromISOExtendedString(" 01:12:17 "), TimeOfDay(1, 12, 17)); + + //Verify Examples. + assert(TimeOfDay.fromISOExtendedString("00:00:00") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOExtendedString("12:30:33") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOExtendedString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); + } + + + //TODO Add function which takes a user-specified time format and produces a TimeOfDay + + //TODO Add function which takes pretty much any time-string and produces a TimeOfDay + // Obviously, it will be less efficient, and it probably won't manage _every_ + // possible date format, but a smart conversion function would be nice. + + + /++ + Returns midnight. + +/ + @property static TimeOfDay min() pure nothrow + { + return TimeOfDay.init; + } + + unittest + { + assert(TimeOfDay.min.hour == 0); + assert(TimeOfDay.min.minute == 0); + assert(TimeOfDay.min.second == 0); + assert(TimeOfDay.min < TimeOfDay.max); + } + + + /++ + Returns one second short of midnight. + +/ + @property static TimeOfDay max() pure nothrow + { + auto tod = TimeOfDay.init; + tod._hour = maxHour; + tod._minute = maxMinute; + tod._second = maxSecond; + + return tod; + } + + unittest + { + assert(TimeOfDay.max.hour == 23); + assert(TimeOfDay.max.minute == 59); + assert(TimeOfDay.max.second == 59); + assert(TimeOfDay.max > TimeOfDay.min); + } + + +private: + + /++ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. If the + number of minutes overflows (or underflows), then the minutes will wrap. + If the number of minutes overflows(or underflows), then the hour will + wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). + + Params: + seconds = The number of seconds to add to this TimeOfDay. + +/ + ref TimeOfDay addSeconds(long seconds) pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_hour); + hnsecs += convert!("minutes", "hnsecs")(_minute); + hnsecs += convert!("seconds", "hnsecs")(_second); + + hnsecs %= convert!("days", "hnsecs")(1); + + if(hnsecs < 0) + hnsecs += convert!("days", "hnsecs")(1); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _hour = cast(ubyte)newHours; + _minute = cast(ubyte)newMinutes; + _second = cast(ubyte)newSeconds; + + return this; + } + + unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig.addSeconds(seconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); + + testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); + testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.addSeconds(7))); + static assert(!__traits(compiles, itod.addSeconds(7))); + } + + + /++ + Whether the given values form a valid TimeOfDay + +/ + static bool _valid(int hour, int minute, int second) pure nothrow + { + return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); + } + + + pure invariant() + { + assert(_valid(_hour, _minute, _second), + "Invariant Failure: hour [" ~ + numToString(_hour) ~ + "] minute [" ~ + numToString(_minute) ~ + "] second [" ~ + numToString(_second) ~ + "]"); + } + + ubyte _hour; + ubyte _minute; + ubyte _second; + + enum ubyte maxHour = 24 - 1; + enum ubyte maxMinute = 60 - 1; + enum ubyte maxSecond = 60 - 1; +} + + +/++ + Combines Date and TimeOfDay structs to give you an object which holds both + the date and the time. It is optimized for calendar operations and has no + concept of time zone. If you want an object which is optimized for time + operations based on the system time, then use SysTime. SysTime has a concept + of time zone and has much higher precision (hnsecs). DateTime is intended + primarily for calendar-based uses rather than precise time operations. + +/ +struct DateTime +{ +public: + + /++ + Params: + date = The date portion of DateTime. + tod = The time portion of DateTime. + +/ + this(in Date date, in TimeOfDay tod = TimeOfDay.init) pure nothrow + { + _date = date; + _tod = tod; + } + + unittest + { + { + auto dt = DateTime.init; + assertPred!"=="(dt._date, Date.init); + assertPred!"=="(dt._tod, TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6)); + assertPred!"=="(dt._date, Date(1999, 7, 6)); + assertPred!"=="(dt._tod, TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); + assertPred!"=="(dt._date, Date(1999, 7, 6)); + assertPred!"=="(dt._tod, TimeOfDay(12, 30, 33)); + } + } + + + /++ + Params: + year = The year portion of the date. + month = The month portion of the date. + day = The day portion of the date. + hour = The hour portion of the time; + minute = The minute portion of the time; + second = The second portion of the time; + +/ + this(int year, int month, int day, + int hour = 0, int minute = 0, int second = 0) pure + { + _date = Date(year, month, day); + _tod = TimeOfDay(hour, minute, second); + } + + unittest + { + { + auto dt = DateTime(1999, 7 ,6); + assertPred!"=="(dt._date, Date(1999, 7, 6)); + assertPred!"=="(dt._tod, TimeOfDay.init); + } + + { + auto dt = DateTime(1999, 7 ,6, 12, 30, 33); + assertPred!"=="(dt._date, Date(1999, 7, 6)); + assertPred!"=="(dt._tod, TimeOfDay(12, 30, 33)); + } + } + + + /++ + Compares this DateTime with the given DateTime. + + Returns: + $(TABLE + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in DateTime rhs) const pure nothrow + { + immutable dateResult = _date.opCmp(rhs._date); + + if(dateResult != 0) + return dateResult; + + return _tod.opCmp(rhs._tod); + } + + unittest + { + //Test A.D. + assertPred!("opCmp", "==")(DateTime(Date.init, TimeOfDay.init), DateTime.init); + + assertPred!("opCmp", "==")(DateTime(Date(1999, 1, 1)), DateTime(Date(1999, 1, 1))); + assertPred!("opCmp", "==")(DateTime(Date(1, 7, 1)), DateTime(Date(1, 7, 1))); + assertPred!("opCmp", "==")(DateTime(Date(1, 1, 6)), DateTime(Date(1, 1, 6))); + + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 1)), DateTime(Date(1999, 7, 1))); + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6)), DateTime(Date(1999, 7, 6))); + + assertPred!("opCmp", "==")(DateTime(Date(1, 7, 6)), DateTime(Date(1, 7, 6))); + + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6)), DateTime(Date(2000, 7, 6))); + assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6)), DateTime(Date(1999, 7, 6))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6)), DateTime(Date(1999, 8, 6))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6)), DateTime(Date(1999, 7, 6))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6)), DateTime(Date(1999, 7, 7))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7)), DateTime(Date(1999, 7, 6))); + + assertPred!("opCmp", "<")(DateTime(Date(1999, 8, 7)), DateTime(Date(2000, 7, 6))); + assertPred!("opCmp", ">")(DateTime(Date(2000, 8, 6)), DateTime(Date(1999, 7, 7))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 7)), DateTime(Date(2000, 7, 6))); + assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6)), DateTime(Date(1999, 7, 7))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 7)), DateTime(Date(1999, 8, 6))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6)), DateTime(Date(1999, 7, 7))); + + + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)), + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))); + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + //Test B.C. + assertPred!("opCmp", "==")(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), + DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "==")(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)), + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "==")(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "==")(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "==")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "==")(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); + + //Test Both + assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))); + + assertPred!("opCmp", "<")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), + DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))); + assertPred!("opCmp", ">")(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)), + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + static assert(__traits(compiles, dt.opCmp(dt))); + static assert(__traits(compiles, dt.opCmp(cdt))); + static assert(__traits(compiles, dt.opCmp(idt))); + static assert(__traits(compiles, cdt.opCmp(dt))); + static assert(__traits(compiles, cdt.opCmp(cdt))); + static assert(__traits(compiles, cdt.opCmp(idt))); + static assert(__traits(compiles, idt.opCmp(dt))); + static assert(__traits(compiles, idt.opCmp(cdt))); + static assert(__traits(compiles, idt.opCmp(idt))); + } + + + /++ + The date portion of DateTime. + +/ + @property Date date() const pure nothrow + { + return _date; + } + + unittest + { + { + auto dt = DateTime.init; + assertPred!"=="(dt.date, Date.init); + } + + { + auto dt = DateTime(Date(1999, 7, 6)); + assertPred!"=="(dt.date, Date(1999, 7, 6)); + } + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(__traits(compiles, cdt.date == Date(2010, 1, 1))); + static assert(__traits(compiles, idt.date == Date(2010, 1, 1))); + } + + + /++ + The date portion of DateTime. + + Params: + date = The Date to set this DateTime's date portion to. + +/ + @property void date(in Date date) pure nothrow + { + _date = date; + } + + unittest + { + auto dt = DateTime.init; + dt.date = Date(1999, 7, 6); + assertPred!"=="(dt._date, Date(1999, 7, 6)); + assertPred!"=="(dt._tod, TimeOfDay.init); + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); + static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); + } + + + /++ + The time portion of DateTime. + +/ + @property TimeOfDay timeOfDay() const pure nothrow + { + return _tod; + } + + unittest + { + { + auto dt = DateTime.init; + assertPred!"=="(dt.timeOfDay, TimeOfDay.init); + } + + { + auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); + assertPred!"=="(dt.timeOfDay, TimeOfDay(12, 30, 33)); + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.timeOfDay == TimeOfDay(12, 30, 33))); + static assert(__traits(compiles, idt.timeOfDay == TimeOfDay(12, 30, 33))); + } + + + /++ + The time portion of DateTime. + + Params: + tod = The TimeOfDay to set this DateTime's time portion to. + +/ + @property void timeOfDay(in TimeOfDay tod) pure nothrow + { + _tod = tod; + } + + unittest + { + auto dt = DateTime.init; + dt.timeOfDay = TimeOfDay(12, 30, 33); + assertPred!"=="(dt._date, date.init); + assertPred!"=="(dt._tod, TimeOfDay(12, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); + static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() const pure nothrow + { + return _date.year; + } + + unittest + { + assertPred!"=="(Date.init.year, 1); + assertPred!"=="(Date(1999, 7, 6).year, 1999); + assertPred!"=="(Date(-1999, 7, 6).year, -1999); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, idt.year)); + static assert(__traits(compiles, idt.year)); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this DateTime's year to. + + Throws: + DateTimeException if the new year is not a leap year and if the + resulting date would be on February 29th. + + Examples: +-------------------- +assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); +assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); +assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); +-------------------- + +/ + @property void year(int year) pure + { + _date.year = year; + } + + unittest + { + static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) + { + dt.year = year; + assertPred!"=="(dt, expected, "", __FILE__, line); + } + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 1999, DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 0, DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), -1999, DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.year = 7)); + static assert(!__traits(compiles, idt.year = 7)); + + //Verify Examples. + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + DateTimeException if $(D isAD) is true. + + Examples: +-------------------- +assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); +assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); +assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); +-------------------- + +/ + @property short yearBC() const pure + { + return _date.yearBC; + } + + unittest + { + assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, dt.yearBC = 12)); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + + //Verify Examples. + assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); + assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); + assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this DateTime's year to. + + Throws: + DateTimeException if a non-positive value is given. + + Examples: +-------------------- +auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); +dt.yearBC = 1; +assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); + +dt.yearBC = 10; +assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); +-------------------- + +/ + @property void yearBC(int year) pure + { + _date.yearBC = year; + } + + unittest + { + assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); + + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, dt.yearBC = 12)); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + //Verify Examples. + { + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); + dt.yearBC = 1; + assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); + + dt.yearBC = 10; + assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); + } + } + + + /++ + Month of a Gregorian Year. + + Examples: +-------------------- +assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); +assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); +assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); +-------------------- + +/ + @property Month month() const pure nothrow + { + return _date.month; + } + + unittest + { + assertPred!"=="(DateTime.init.month, 1); + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month, 7); + assertPred!"=="(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month, 7); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.month)); + static assert(__traits(compiles, idt.month)); + + //Verify Examples. + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this DateTime's month to. + + Throws: + DateTimeException if the given month is not a valid month. + +/ + @property void month(Month month) pure + { + _date.month = month; + } + + unittest + { + static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) + { + dt.month = month; + assert(expected != DateTime.init); + assertPred!"=="(dt, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)13)); + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.month = 12)); + static assert(!__traits(compiles, idt.month = 12)); + } + + + /++ + Day of a Gregorian Month. + + Examples: +-------------------- +assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); +assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); +assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); +-------------------- + +/ + @property ubyte day() const pure nothrow + { + return _date.day; + } + + unittest + { + assertPred!"=="(DateTime.init.day, 1); + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).day, 6); + assertPred!"=="(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).day, 6); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.day)); + static assert(__traits(compiles, idt.day)); + + //Verify Examples. + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this DateTime's day to. + + Throws: + DateTimeException if the given day is not a valid day of the current month. + +/ + @property void day(int day) pure + { + _date.day = day; + } + + unittest + { + static void testDT(DateTime dt, int day) + { + dt.day = day; + } + + //Test A.D. + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); + + { + auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assertPred!"=="(dt, DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); + } + + //Test B.C. + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); + + auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assertPred!"=="(dt, DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.day = 27)); + static assert(!__traits(compiles, idt.day = 27)); + } + + + /++ + Hours passed midnight. + +/ + @property ubyte hour() const pure nothrow + { + return _tod.hour; + } + + unittest + { + assertPred!"=="(DateTime.init.hour, 0); + assertPred!"=="(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour, 12); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.hour)); + static assert(__traits(compiles, idt.hour)); + } + + + /++ + Hours passed midnight. + + Params: + hour = The hour of the day to set this DateTime's hour to. + + Throws: + DateTimeException if the given hour would result in an invalid + DateTime. + +/ + @property void hour(int hour) pure + { + _tod.hour = hour; + } + + unittest + { + assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); + + auto dt = DateTime.init; + dt.hour = 12; + assertPred!"=="(dt, DateTime(1, 1, 1, 12, 0, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.hour = 27)); + static assert(!__traits(compiles, idt.hour = 27)); + } + + + /++ + Minutes passed the hour. + +/ + @property ubyte minute() const pure nothrow + { + return _tod.minute; + } + + unittest + { + assertPred!"=="(DateTime.init.minute, 0); + assertPred!"=="(DateTime(1, 1, 1, 0, 30, 0).minute, 30); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.minute)); + static assert(__traits(compiles, idt.minute)); + } + + + /++ + Minutes passed the hour. + + Params: + minute = The minute to set this DateTime's minute to. + + Throws: + DateTimeException if the given minute would result in an invalid + DateTime. + +/ + @property void minute(int minute) pure + { + _tod.minute = minute; + } + + unittest + { + assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); + + auto dt = DateTime.init; + dt.minute = 30; + assertPred!"=="(dt, DateTime(1, 1, 1, 0, 30, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.minute = 27)); + static assert(!__traits(compiles, idt.minute = 27)); + } + + + /++ + Seconds passed the minute. + +/ + @property ubyte second() const pure nothrow + { + return _tod.second; + } + + unittest + { + assertPred!"=="(DateTime.init.second, 0); + assertPred!"=="(DateTime(1, 1, 1, 0, 0, 33).second, 33); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.second)); + static assert(__traits(compiles, idt.second)); + } + + + /++ + Seconds passed the minute. + + Params + second = The second to set this DateTime's second to. + + Throws: + DateTimeException if the given seconds would result in an invalid + DateTime. + +/ + @property void second(int second) pure + { + _tod.second = second; + } + + unittest + { + assertThrown!DateTimeException((){DateTime.init.second = 60;}()); + + auto dt = DateTime.init; + dt.second = 33; + assertPred!"=="(dt, DateTime(1, 1, 1, 0, 0, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.second = 27)); + static assert(!__traits(compiles, idt.second = 27)); + } + + + /++ + Adds the given number of years or months to this DateTime. 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 days 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 DateTime. + allowOverflow = Whether the days should be allowed to overflow, causing + the month to increment. + + Examples: +-------------------- +auto dt1 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 30, 33)); +dt1.add!"months"(11); +assert(dt1 == DateTime(Date(2010, 12, 1), TimeOfDay(12, 30, 33))); + +auto dt2 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 30, 33)); +dt2.add!"months"(-11); +assert(dt2 == DateTime(Date(2009, 2, 1), TimeOfDay(12, 30, 33))); + +auto dt3 = DateTime(Date(2000, 2, 29), TimeOfDay(12, 30, 33)); +dt3.add!"years"(1); +assert(dt3 == DateTime(Date(2001, 3, 1), TimeOfDay(12, 30, 33))); + +auto dt4 = DateTime(Date(2000, 2, 29), TimeOfDay(12, 30, 33)); +dt4.add!"years"(1, AllowDayOverflow.no); +assert(dt4 == DateTime(Date(2001, 2, 28), TimeOfDay(12, 30, 33))); +-------------------- + +/ + /+ref DateTime+/ void add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + if(units == "years" || + units == "months") + { + _date.add!units(value, allowOverflow); + } + + unittest + { + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.add!"years"(4))); + static assert(!__traits(compiles, idt.add!"years"(4))); + static assert(!__traits(compiles, cdt.add!"months"(4))); + static assert(!__traits(compiles, idt.add!"months"(4))); + + //Verify Examples. + auto dt1 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 30, 33)); + dt1.add!"months"(11); + assert(dt1 == DateTime(Date(2010, 12, 1), TimeOfDay(12, 30, 33))); + + auto dt2 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 30, 33)); + dt2.add!"months"(-11); + assert(dt2 == DateTime(Date(2009, 2, 1), TimeOfDay(12, 30, 33))); + + auto dt3 = DateTime(Date(2000, 2, 29), TimeOfDay(12, 30, 33)); + dt3.add!"years"(1); + assert(dt3 == DateTime(Date(2001, 3, 1), TimeOfDay(12, 30, 33))); + + auto dt4 = DateTime(Date(2000, 2, 29), TimeOfDay(12, 30, 33)); + dt4.add!"years"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(Date(2001, 2, 28), TimeOfDay(12, 30, 33))); + } + + + /++ + Adds the given number of years or months this DateTime. A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. So, if you roll the DateTime 12 months, you get + the exact same Date. However, the days can still be affected due to + the differing number of days in each month. + + Params: + units = The units to roll. + value = The number of units to add to this DateTime. + allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. + + Examples: +-------------------- +auto dt1 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 33, 33)); +dt1.roll!"years"(1); +assert(dt1 == DateTime(Date(2011, 1, 1), TimeOfDay(12, 33, 33))); + +auto dt2 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 33, 33)); +dt2.roll!"months"(-12); +assert(dt2 == DateTime(Date(2010, 1, 1), TimeOfDay(12, 33, 33))); + +auto dt3 = DateTime(Date(1999, 1, 29), TimeOfDay(12, 33, 33)); +dt3.roll!"months"(1); +assert(dt3 == DateTime(Date(1999, 3, 1), TimeOfDay(12, 33, 33))); + +auto dt4 = DateTime(Date(1999, 1, 29), TimeOfDay(12, 33, 33)); +dt4.roll!"months"(1, AllowDayOverflow.no); +assert(dt4 == DateTime(Date(1999, 2, 28), TimeOfDay(12, 33, 33))); +-------------------- + +/ + /+ref DateTime+/ void roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + if(units == "years" || + units == "months") + { + _date.roll!units(value, allowOverflow); + } + + unittest + { + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"years"(4))); + static assert(!__traits(compiles, idt.roll!"years"(4))); + static assert(!__traits(compiles, cdt.roll!"months"(4))); + static assert(!__traits(compiles, idt.roll!"months"(4))); + static assert(!__traits(compiles, cdt.roll!"days"(4))); + static assert(!__traits(compiles, idt.roll!"days"(4))); + + //Verify Examples + auto dt1 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 33, 33)); + dt1.roll!"years"(1); + assert(dt1 == DateTime(Date(2011, 1, 1), TimeOfDay(12, 33, 33))); + + auto dt2 = DateTime(Date(2010, 1, 1), TimeOfDay(12, 33, 33)); + dt2.roll!"months"(-12); + assert(dt2 == DateTime(Date(2010, 1, 1), TimeOfDay(12, 33, 33))); + + auto dt3 = DateTime(Date(1999, 1, 29), TimeOfDay(12, 33, 33)); + dt3.roll!"months"(1); + assert(dt3 == DateTime(Date(1999, 3, 1), TimeOfDay(12, 33, 33))); + + auto dt4 = DateTime(Date(1999, 1, 29), TimeOfDay(12, 33, 33)); + dt4.roll!"months"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(Date(1999, 2, 28), TimeOfDay(12, 33, 33))); + } + + + /++ + Adds the given number of days this DateTime. A negative number will subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the Date one year's worth of days, then you + get the exact same Date. + + Note that there is no $(D add!"days"()) because you can add days to a DateTime + by adding a duration to it. + + Params: + days = The number of days to add to this DateTime. + + Examples: +-------------------- +auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(11, 23, 12)); +dt.roll!"days"(1); +assert(dt == DateTime(Date(2010, 1, 2), TimeOfDay(11, 23, 12))); +dt.roll!"days"(365); +assert(dt == DateTime(Date(2010, 1, 26), TimeOfDay(11, 23, 12))); +dt.roll!"days"(-32); +assert(dt == DateTime(Date(2010, 1, 25), TimeOfDay(11, 23, 12))); +-------------------- + +/ + /+ref DateTime+/ void roll(string units)(long days) pure nothrow + if(units == "days") + { + _date.roll!"days"(days); + } + + unittest + { + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"days"(4))); + static assert(!__traits(compiles, idt.roll!"days"(4))); + + //Verify Examples. + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(11, 23, 12)); + dt.roll!"days"(1); + assert(dt == DateTime(Date(2010, 1, 2), TimeOfDay(11, 23, 12))); + dt.roll!"days"(365); + assert(dt == DateTime(Date(2010, 1, 26), TimeOfDay(11, 23, 12))); + dt.roll!"days"(-32); + assert(dt == DateTime(Date(2010, 1, 25), TimeOfDay(11, 23, 12))); + } + + + /++ + Add the given number of the given units to the time of day. Negative + values will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. So, if you roll the DateTime 24 hours, you get + the exact same DateTime. + + Note that there is no $(D add!"hours"()), $(D add!"minutes"()), or + $(D add!"seconds"()) function because you can add them to a DateTime by + adding a duration to it. + + Params: + units = The units to roll. + value = The number of units to add to this DateTime. + + Examples: +-------------------- +auto dt1 = DateTime(Date(2010, 7, 4), TimeOfDay(12, 0, 0)); +dt1.roll!"hours"(1); +assert(dt1 == DateTime(Date(2010, 7, 4), TimeOfDay(13, 0, 0))); + +auto dt2 = DateTime(Date(2010, 2, 12), TimeOfDay(12, 0, 0)); +dt2.roll!"hours"(-1); +assert(dt2 == DateTime(Date(2010, 2, 12), TimeOfDay(11, 0, 0))); + +auto dt3 = DateTime(Date(2009, 12, 31), TimeOfDay(23, 0, 0)); +dt3.roll!"hours"(1); +assert(dt3 == DateTime(Date(2009, 12, 31), TimeOfDay(0, 0, 0))); + +auto dt4 = DateTime(Date(2010, 1, 1), TimeOfDay(0, 0, 0)); +dt4.roll!"hours"(-1); +assert(dt4 == DateTime(Date(2010, 1, 1), TimeOfDay(23, 0, 0))); +-------------------- + +/ + /+ref DateTime+/ void roll(string units)(long value) pure nothrow + if(units == "hours" || + units == "minutes" || + units == "seconds") + { + _tod.roll!units(value); + } + + //Test roll!"hours"(). + unittest + { + static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); + + //Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); + + //Test Both + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"hours"(4))); + static assert(!__traits(compiles, idt.roll!"hours"(4))); + + //Verify Examples. + auto dt1 = DateTime(Date(2010, 7, 4), TimeOfDay(12, 0, 0)); + dt1.roll!"hours"(1); + assert(dt1 == DateTime(Date(2010, 7, 4), TimeOfDay(13, 0, 0))); + + auto dt2 = DateTime(Date(2010, 2, 12), TimeOfDay(12, 0, 0)); + dt2.roll!"hours"(-1); + assert(dt2 == DateTime(Date(2010, 2, 12), TimeOfDay(11, 0, 0))); + + auto dt3 = DateTime(Date(2009, 12, 31), TimeOfDay(23, 0, 0)); + dt3.roll!"hours"(1); + assert(dt3 == DateTime(Date(2009, 12, 31), TimeOfDay(0, 0, 0))); + + auto dt4 = DateTime(Date(2010, 1, 1), TimeOfDay(0, 0, 0)); + dt4.roll!"hours"(-1); + assert(dt4 == DateTime(Date(2010, 1, 1), TimeOfDay(23, 0, 0))); + } + + //Test roll!"minutes"(). + unittest + { + static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); + + //Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); + + //Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"minutes"(4))); + static assert(!__traits(compiles, idt.roll!"minutes"(4))); + } + + //Test roll!"seconds"(). + unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); + + //Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); + + //Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"seconds"(4))); + static assert(!__traits(compiles, idt.roll!"seconds"(4))); + } + + + /++ + Gives the result of adding or subtracting a duration from this DateTime. + + The legal types of arithmetic for DateTime using this operator are + + $(TABLE + $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The duration to add to or subtract from this DateTime. + +/ + DateTime opBinary(string op, D)(in D duration) const pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + DateTime retval = this; + + static if(is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if(is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + //Ideally, this would just be + //return retval.addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedHNSecs = hnsecs; + else static if(op == "-") + immutable signedHNSecs = -hnsecs; + else + static assert(0); + + return retval.addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assertPred!"=="(dt + dur!"weeks"(7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assertPred!"=="(dt + dur!"weeks"(-7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assertPred!"=="(dt + dur!"days"(7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assertPred!"=="(dt + dur!"days"(-7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assertPred!"=="(dt + dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assertPred!"=="(dt + dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assertPred!"=="(dt + dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assertPred!"=="(dt + dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assertPred!"=="(dt + dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt + dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"=="(dt + dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt + dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"=="(dt + dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt + dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"=="(dt + dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt + dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(dt + TickDuration.from!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt + TickDuration.from!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + + assertPred!"=="(dt - dur!"weeks"(-7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assertPred!"=="(dt - dur!"weeks"(7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assertPred!"=="(dt - dur!"days"(-7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assertPred!"=="(dt - dur!"days"(7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assertPred!"=="(dt - dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assertPred!"=="(dt - dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assertPred!"=="(dt - dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assertPred!"=="(dt - dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assertPred!"=="(dt - dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt - dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"=="(dt - dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt - dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"=="(dt - dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt - dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"=="(dt - dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt - dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(dt - TickDuration.from!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"=="(dt - TickDuration.from!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt + duration)); + static assert(__traits(compiles, idt + duration)); + static assert(__traits(compiles, cdt - duration)); + static assert(__traits(compiles, idt - duration)); + } + + + /++ + Gives the result of adding or subtracting a duration from this DateTime, + as well as assigning the result to this DateTime. + + The legal types of arithmetic for DateTime using this operator are + + $(TABLE + $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The duration to add to or subtract from this DateTime. + +/ + /+ref+/ DateTime opOpAssign(string op, D)(in D duration) pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + DateTime retval = this; + + static if(is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if(is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + //Ideally, this would just be + //return addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedHNSecs = hnsecs; + else static if(op == "-") + immutable signedHNSecs = -hnsecs; + else + static assert(0); + + return addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + } + + unittest + { + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(-7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(-7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(-7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(-7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt += duration)); + static assert(!__traits(compiles, idt += duration)); + static assert(!__traits(compiles, cdt -= duration)); + static assert(!__traits(compiles, idt -= duration)); + } + + + /++ + Gives the difference between two DateTimes. + + The legal types of arithmetic for DateTime using this operator are + + $(TABLE + $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in DateTime rhs) const pure nothrow + if(op == "-") + { + immutable dateResult = _date - rhs.date; + immutable todResult = _tod - rhs._tod; + + return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); + } + + unittest + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(31_536_000)); + assertPred!"=="(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(-31_536_000)); + + assertPred!"=="(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(26_78_400)); + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(-26_78_400)); + + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)), + dur!"seconds"(86_400)); + assertPred!"=="(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(-86_400)); + + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)), + dur!"seconds"(3600)); + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(-3600)); + + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(60)); + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), + dur!"seconds"(-60)); + + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), + dur!"seconds"(1)); + assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), + dur!"seconds"(-1)); + + assertPred!"=="(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0), dur!"seconds"(45033)); + assertPred!"=="(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33), dur!"seconds"(-45033)); + assertPred!"=="(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0), dur!"seconds"(-41367)); + assertPred!"=="(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33), dur!"seconds"(41367)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt - dt)); + static assert(__traits(compiles, cdt - dt)); + static assert(__traits(compiles, idt - dt)); + + static assert(__traits(compiles, dt - cdt)); + static assert(__traits(compiles, cdt - cdt)); + static assert(__traits(compiles, idt - cdt)); + + static assert(__traits(compiles, dt - idt)); + static assert(__traits(compiles, cdt - idt)); + static assert(__traits(compiles, idt - idt)); + } + + + /++ + Returns the difference between the two DateTimes in months. + + You can get the difference in years by subtracting the year property + of two DateTimes, and you can get the difference in days or weeks by + subtracting the DateTimes themselves and using the Duration that results, + but because you cannot convert between months and smaller units without + a specific date (which Durations don't have), you cannot get the difference + in months without doing some math using both the year and month properties, + so this is a convenience function for getting the difference in months. + + Note that the number of days in the months or how far into the month either + date is is irrelevant. It is the difference in the month property combined + with the difference in years * 12. So, for instance, December 31st and + January 1st are one month apart just as December 1st and January 31st are + one month apart. + + Params: + rhs = The DateTime to subtract from this one. + + Examples: +-------------------- +assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(DateTime(1999, 1, 31, 23, 59, 59)) == 1); +assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(DateTime(1999, 2, 1, 12, 3, 42)) == -1); +assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(DateTime(1999, 1, 1, 2, 4, 7)) == 2); +assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(DateTime(1999, 3, 31, 0, 30, 58)) == -2); +-------------------- + +/ + int diffMonths(in DateTime rhs) const pure nothrow + { + return _date.diffMonths(rhs._date); + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.diffMonths(dt))); + static assert(__traits(compiles, cdt.diffMonths(dt))); + static assert(__traits(compiles, idt.diffMonths(dt))); + + static assert(__traits(compiles, dt.diffMonths(cdt))); + static assert(__traits(compiles, cdt.diffMonths(cdt))); + static assert(__traits(compiles, idt.diffMonths(cdt))); + + static assert(__traits(compiles, dt.diffMonths(idt))); + static assert(__traits(compiles, cdt.diffMonths(idt))); + static assert(__traits(compiles, idt.diffMonths(idt))); + + //Verify Examples. + assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(DateTime(1999, 1, 31, 23, 59, 59)) == 1); + assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(DateTime(1999, 2, 1, 12, 3, 42)) == -1); + assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(DateTime(1999, 1, 1, 2, 4, 7)) == 2); + assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(DateTime(1999, 3, 31, 0, 30, 58)) == -2); + } + + + /++ + Whether this DateTime is in a leap year. + +/ + @property bool isLeapYear() const pure nothrow + { + return _date.isLeapYear; + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.isLeapYear)); + static assert(__traits(compiles, cdt.isLeapYear)); + static assert(__traits(compiles, idt.isLeapYear)); + } + + + /++ + Day of the week this DateTime is on. + +/ + @property DayOfWeek dayOfWeek() const pure nothrow + { + return _date.dayOfWeek; + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.dayOfWeek)); + static assert(__traits(compiles, cdt.dayOfWeek)); + static assert(__traits(compiles, idt.dayOfWeek)); + } + + + /++ + Day of the year this DateTime is on. + + Examples: +-------------------- +assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); +assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); +assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); +-------------------- + +/ + @property ushort dayOfYear() const pure nothrow + { + return _date.dayOfYear; + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.dayOfYear)); + static assert(__traits(compiles, cdt.dayOfYear)); + static assert(__traits(compiles, idt.dayOfYear)); + + //Verify Examples. + assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); + assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); + assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this DateTime is on. + +/ + @property void dayOfYear(int day) pure + { + _date.dayOfYear = day; + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.dayOfYear = 12)); + static assert(!__traits(compiles, cdt.dayOfYear = 12)); + static assert(!__traits(compiles, idt.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this DateTime is on. + + Examples: +-------------------- +assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); +assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); +assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); + +assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); +assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); +assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); + +assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); +assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); +-------------------- + +/ + @property int dayOfGregorianCal() const pure nothrow + { + return _date.dayOfGregorianCal; + } + + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.dayOfGregorianCal)); + static assert(__traits(compiles, idt.dayOfGregorianCal)); + + //Verify Examples. + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); + assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); + assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); + assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); + assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); + + assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); + } + + + /++ + The Xth day of the Gregorian Calendar that this DateTime is on. Setting + this property does not affect the time portion of DateTime. + + Params: + days = The day of the Gregorian Calendar to set this DateTime to. + + Examples: +-------------------- +auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); +dt.dayOfGregorianCal = 1; +assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = 365; +assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = 366; +assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = 0; +assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = -365; +assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = -366; +assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = 730_120; +assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); + +dt.dayOfGregorianCal = 734_137; +assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); +-------------------- + +/ + @property void dayOfGregorianCal(int days) pure nothrow + { + _date.dayOfGregorianCal = days; + } + + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); + static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); + + //Verify Examples. + auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); + dt.dayOfGregorianCal = 1; + assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 365; + assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 366; + assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 0; + assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -365; + assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -366; + assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 730_120; + assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 734_137; + assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); + } + + + /++ + The ISO 8601 week of the year that this DateTime is in. + + See_Also: + ISO Week Date + +/ + @property ubyte isoWeek() const pure nothrow + { + return _date.isoWeek; + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.isoWeek)); + static assert(__traits(compiles, cdt.isoWeek)); + static assert(__traits(compiles, idt.isoWeek)); + } + + + /++ + DateTime for the last day in the month that this DateTime is in. + The time portion of endOfMonth is always 23:59:59. + + Examples: +-------------------- +assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); +assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); +assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); +assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); +-------------------- + +/ + @property DateTime endOfMonth() const pure nothrow + { + try + return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); + catch(Exception e) + assert(0, "DateTime constructor threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth, DateTime(1999, 1, 31, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth, DateTime(1999, 2, 28, 23, 59, 59)); + assertPred!"=="(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth, DateTime(2000, 2, 29, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth, DateTime(1999, 3, 31, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth, DateTime(1999, 4, 30, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth, DateTime(1999, 5, 31, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth, DateTime(1999, 6, 30, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth, DateTime(1999, 7, 31, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth, DateTime(1999, 8, 31, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth, DateTime(1999, 9, 30, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth, DateTime(1999, 10, 31, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth, DateTime(1999, 11, 30, 23, 59, 59)); + assertPred!"=="(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth, DateTime(1999, 12, 31, 23, 59, 59)); + + //Test B.C. + assertPred!"=="(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth, DateTime(-1999, 1, 31, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth, DateTime(-1999, 2, 28, 23, 59, 59)); + assertPred!"=="(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth, DateTime(-2000, 2, 29, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth, DateTime(-1999, 3, 31, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth, DateTime(-1999, 4, 30, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth, DateTime(-1999, 5, 31, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth, DateTime(-1999, 6, 30, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth, DateTime(-1999, 7, 31, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth, DateTime(-1999, 8, 31, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth, DateTime(-1999, 9, 30, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth, DateTime(-1999, 10, 31, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth, DateTime(-1999, 11, 30, 23, 59, 59)); + assertPred!"=="(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth, DateTime(-1999, 12, 31, 23, 59, 59)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.endOfMonth)); + static assert(__traits(compiles, idt.endOfMonth)); + + //Verify Examples. + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); + } + + + /++ + The last day in the month that this DateTime is in. + + Examples: +-------------------- +assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonthDay == 31); +assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonthDay == 28); +assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonthDay == 29); +assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonthDay == 30); +-------------------- + +/ + @property ubyte endOfMonthDay() const pure nothrow + { + return _date.endOfMonthDay; + } + + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.endOfMonthDay)); + static assert(__traits(compiles, idt.endOfMonthDay)); + + //Verify Examples. + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonthDay == 31); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonthDay == 28); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonthDay == 29); + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonthDay == 30); + } + + + /++ + Whether the current year is a date in A.D. + + Examples: +-------------------- +assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); +assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); +assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); +assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); +-------------------- + +/ + @property bool isAD() const pure nothrow + { + return _date.isAD; + } + + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.isAD)); + static assert(__traits(compiles, idt.isAD)); + + //Verify Examples. + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); + assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); + assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); + } + + + /++ + The julian day for this DateTime at the given time. For example, + prior to noon, 1996-03-31 would be the julian day number 2_450_173, + so this function returns 2_450_173, while from noon onward, it the + julian day number would be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() const pure nothrow + { + if(_tod._hour < 12) + return _date.julianDay - 1; + else + return _date.julianDay; + } + + unittest + { + assertPred!"=="(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay, -1); + assertPred!"=="(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay, 0); + + assertPred!"=="(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay, 1_721_424); + assertPred!"=="(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay, 1_721_425); + + assertPred!"=="(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay, 1_721_425); + assertPred!"=="(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay, 1_721_426); + + assertPred!"=="(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay, 2_299_160); + assertPred!"=="(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay, 2_299_161); + + assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay, 2_400_000); + assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay, 2_400_001); + + assertPred!"=="(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay, 2_444_973); + assertPred!"=="(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay, 2_444_974); + + assertPred!"=="(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay, 2_450_173); + assertPred!"=="(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay, 2_450_174); + + assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay, 2_455_432); + assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay, 2_455_433); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.julianDay)); + static assert(__traits(compiles, idt.julianDay)); + } + + + /++ + The modified julian day for any time on this Date (since, the modified + julian day changes at midnight). + +/ + @property long modJulianDay() const pure nothrow + { + return _date.modJulianDay; + } + + unittest + { + assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay, 0); + assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay, 0); + + assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay, 55_432); + assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay, 55_432); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.modJulianDay)); + static assert(__traits(compiles, idt.modJulianDay)); + } + + + /++ + Converts this DateTime to a string with the format YYYYMMDDTHHMMSS. + + Examples: +-------------------- +assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == "20100704T070612"); +assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == "19981225T021500"); +assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == "00000105T230959"); +assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == "-00040105T000002"); +-------------------- + +/ + string toISOString() const nothrow + { + try + return format("%sT%s", _date.toISOString(), _tod.toISOString()); + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString(), "00091204T000000"); + assertPred!"=="(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString(), "00991204T050612"); + assertPred!"=="(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString(), "09991204T134459"); + assertPred!"=="(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString(), "99990704T235959"); + assertPred!"=="(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString(), "+100001020T010101"); + + //Test B.C. + assertPred!"=="(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString(), "00001204T001204"); + assertPred!"=="(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString(), "-00091204T000000"); + assertPred!"=="(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString(), "-00991204T050612"); + assertPred!"=="(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString(), "-09991204T134459"); + assertPred!"=="(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString(), "-99990704T235959"); + assertPred!"=="(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString(), "-100001020T010101"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.toISOString())); + static assert(__traits(compiles, idt.toISOString())); + + //Verify Examples. + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == "20100704T070612"); + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == "19981225T021500"); + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == "00000105T230959"); + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == "-00040105T000002"); + } + + + /++ + Converts this DateTime to a string with the format YYYY-MM-DDTHH:MM:SS. + + Examples: +-------------------- +assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtendedString() == "2010-07-04T07:06:12"); +assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtendedString() == "1998-12-25T02:15:00"); +assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtendedString() == "0000-01-05T23:09:59"); +assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtendedString() == "-0004-01-05T00:00:02"); +-------------------- + +/ + string toISOExtendedString() const nothrow + { + try + return format("%sT%s", _date.toISOExtendedString(), _tod.toISOExtendedString()); + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtendedString(), "0009-12-04T00:00:00"); + assertPred!"=="(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtendedString(), "0099-12-04T05:06:12"); + assertPred!"=="(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtendedString(), "0999-12-04T13:44:59"); + assertPred!"=="(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtendedString(), "9999-07-04T23:59:59"); + assertPred!"=="(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtendedString(), "+10000-10-20T01:01:01"); + + //Test B.C. + assertPred!"=="(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtendedString(), "0000-12-04T00:12:04"); + assertPred!"=="(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtendedString(), "-0009-12-04T00:00:00"); + assertPred!"=="(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtendedString(), "-0099-12-04T05:06:12"); + assertPred!"=="(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtendedString(), "-0999-12-04T13:44:59"); + assertPred!"=="(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtendedString(), "-9999-07-04T23:59:59"); + assertPred!"=="(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtendedString(), "-10000-10-20T01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.toISOExtendedString())); + static assert(__traits(compiles, idt.toISOExtendedString())); + + //Verify Examples. + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtendedString() == "2010-07-04T07:06:12"); + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtendedString() == "1998-12-25T02:15:00"); + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtendedString() == "0000-01-05T23:09:59"); + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtendedString() == "-0004-01-05T00:00:02"); + } + + /++ + Converts this DateTime to a string with the format YYYY-Mon-DD HH:MM:SS. + + Examples: +-------------------- +assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); +assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == "1998-Dec-25 02:15:00"); +assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == "0000-Jan-05 23:09:59"); +assert(DateTime(Dte(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == "-0004-Jan-05 00:00:02"); +-------------------- + +/ + string toSimpleString() const nothrow + { + try + return format("%s %s", _date.toSimpleString(), _tod.toString()); + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString(), "0009-Dec-04 00:00:00"); + assertPred!"=="(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString(), "0099-Dec-04 05:06:12"); + assertPred!"=="(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString(), "0999-Dec-04 13:44:59"); + assertPred!"=="(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString(), "9999-Jul-04 23:59:59"); + assertPred!"=="(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString(), "+10000-Oct-20 01:01:01"); + + //Test B.C. + assertPred!"=="(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString(), "0000-Dec-04 00:12:04"); + assertPred!"=="(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString(), "-0009-Dec-04 00:00:00"); + assertPred!"=="(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString(), "-0099-Dec-04 05:06:12"); + assertPred!"=="(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString(), "-0999-Dec-04 13:44:59"); + assertPred!"=="(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString(), "-9999-Jul-04 23:59:59"); + assertPred!"=="(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString(), "-10000-Oct-20 01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.toSimpleString())); + static assert(__traits(compiles, idt.toSimpleString())); + + //Verify Examples. + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == "1998-Dec-25 02:15:00"); + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == "0000-Jan-05 23:09:59"); + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == "-0004-Jan-05 00:00:02"); + } + + + /+ + Converts this DateTime to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return toSimpleString(); + } + + /++ + Converts this DateTime to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return toSimpleString(); + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.toString())); + static assert(__traits(compiles, cdt.toString())); + static assert(__traits(compiles, idt.toString())); + } + + + + /++ + Creates a DateTime from a string with the format YYYYMMDDTHHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates and times. + + Throws: + DateTimeException if the given string is not in the ISO format or if + the resulting DateTime would not be valid. + + Examples: +-------------------- +assert(DateTime.fromISOString("20100704T070612") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); +assert(DateTime.fromISOString("19981225T021500") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); +assert(DateTime.fromISOString("00000105T230959") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); +assert(DateTime.fromISOString("-00040105T000002") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); +assert(DateTime.fromISOString(" 20100704T070612 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); +-------------------- + +/ + static DateTime fromISOString(S)(in S isoString) + if(isSomeString!S) + { + immutable dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); + auto t = dstr.indexOf('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + immutable date = Date.fromISOString(dstr[0..t]); + immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); + + assertPred!"=="(DateTime.fromISOString("20101222T172201"), DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assertPred!"=="(DateTime.fromISOString("19990706T123033"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOString("-19990706T123033"), DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOString("+019990706T123033"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOString("19990706T123033 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOString(" 19990706T123033"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOString(" 19990706T123033 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + //Verify Examples. + assert(DateTime.fromISOString("20100704T070612") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromISOString("19981225T021500") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromISOString("00000105T230959") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromISOString("-00040105T000002") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromISOString(" 20100704T070612 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + + /++ + Creates a DateTime from a string with the format YYYY-MM-DDTHH:MM:SS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO Extended format for dates + and times. + + Throws: + DateTimeException if the given string is not in the ISO Extended + format or if the resulting DateTime would not be valid. + + Examples: +-------------------- +assert(DateTime.fromISOExtendedString("2010-07-04T07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); +assert(DateTime.fromISOExtendedString("1998-12-25T02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); +assert(DateTime.fromISOExtendedString("0000-01-05T23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); +assert(DateTime.fromISOExtendedString("-0004-01-05T00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); +assert(DateTime.fromISOExtendedString(" 2010-07-04T07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); +-------------------- + +/ + static DateTime fromISOExtendedString(S)(in S isoExtString) + if(isSomeString!(S)) + { + immutable dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + auto t = dstr.indexOf('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + immutable date = Date.fromISOExtendedString(dstr[0..t]); + immutable tod = TimeOfDay.fromISOExtendedString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + unittest + { + assertThrown!DateTimeException(DateTime.fromISOExtendedString("")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-07:0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtendedString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromISOExtendedString("2010-Dec-22 17:22:01")); + + assertPred!"=="(DateTime.fromISOExtendedString("2010-12-22T17:22:01"), DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assertPred!"=="(DateTime.fromISOExtendedString("1999-07-06T12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOExtendedString("-1999-07-06T12:30:33"), DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOExtendedString("+01999-07-06T12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOExtendedString("1999-07-06T12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOExtendedString(" 1999-07-06T12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromISOExtendedString(" 1999-07-06T12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + //Verify Examples. + assert(DateTime.fromISOExtendedString("2010-07-04T07:06:12") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromISOExtendedString("1998-12-25T02:15:00") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromISOExtendedString("0000-01-05T23:09:59") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromISOExtendedString("-0004-01-05T00:00:02") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromISOExtendedString(" 2010-07-04T07:06:12 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + + /++ + Creates a DateTime from a string with the format YYYY-Mon-DD HH:MM:SS. + Whitespace is stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString() + formats dates and times. + + Throws: + DateTimeException if the given string is not in the correct format or if + the resulting DateTime would not be valid. + + Examples: +-------------------- +assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); +assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); +assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); +assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); +assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); +-------------------- + +/ + static DateTime fromSimpleString(S)(in S simpleString) + if(isSomeString!(S)) + { + immutable dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); + auto t = dstr.indexOf(' '); + + enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); + + immutable date = Date.fromSimpleString(dstr[0..t]); + immutable tod = TimeOfDay.fromISOExtendedString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); + + assertPred!"=="(DateTime.fromSimpleString("2010-Dec-22 17:22:01"), DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assertPred!"=="(DateTime.fromSimpleString("1999-Jul-06 12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromSimpleString("-1999-Jul-06 12:30:33"), DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromSimpleString("+01999-Jul-06 12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromSimpleString("1999-Jul-06 12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertPred!"=="(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + //Verify Examples. + assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + + //TODO Add function which takes a user-specified time format and produces a DateTime + + //TODO Add function which takes pretty much any time-string and produces a DateTime + // Obviously, it will be less efficient, and it probably won't manage _every_ + // possible date format, but a smart conversion function would be nice. + + + /++ + Returns the DateTime farthest in the past which is representable by DateTime. + +/ + @property static DateTime min() pure nothrow + out(result) + { + assert(result._date == Date.min); + assert(result._tod == TimeOfDay.min); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.min; + dt._date._month = Month.jan; + dt._date._day = 1; + + return dt; + } + + unittest + { + assert(DateTime.min.year < 0); + assert(DateTime.min < DateTime.max); + } + + + /++ + Returns the DateTime farthest in the future which is representable by DateTime. + +/ + @property static DateTime max() pure nothrow + out(result) + { + assert(result._date == Date.max); + assert(result._tod == TimeOfDay.max); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.max; + dt._date._month = Month.dec; + dt._date._day = 31; + dt._tod._hour = TimeOfDay.maxHour; + dt._tod._minute = TimeOfDay.maxMinute; + dt._tod._second = TimeOfDay.maxSecond; + + return dt; + } + + unittest + { + assert(DateTime.max.year > 0); + assert(DateTime.max > DateTime.min); + } + + +private: + + /++ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. The + same goes for any larger units. + + Params: + seconds = The number of seconds to add to this DateTime. + +/ + ref DateTime addSeconds(long seconds) pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_tod._hour); + hnsecs += convert!("minutes", "hnsecs")(_tod._minute); + hnsecs += convert!("seconds", "hnsecs")(_tod._second); + + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + + if(hnsecs < 0) + { + hnsecs += convert!("days", "hnsecs")(1); + --days; + } + + _date.addDays(days); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _tod._hour = cast(ubyte)newHours; + _tod._minute = cast(ubyte)newMinutes; + _tod._second = cast(ubyte)newSeconds; + + return this; + } + + unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig.addSeconds(seconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); + + testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); + + //Test B.C. + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); + + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); + + //Test Both + testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); + testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); + + testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); + testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); + testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); + testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.addSeconds(4))); + static assert(!__traits(compiles, idt.addSeconds(4))); + } + + + Date _date; + TimeOfDay _tod; +} + + + +/++ + SysTime is the type used when you want to get the current time from the system + or if you're doing anything that involves time zones. Unlike DateTime, time + zone is an integral part of SysTime (though if all you care about is local + time, you can pretty much ignore time zones, 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 DateTime, it is not optimized + for calendar operations, and getting individual units from it such as years + or days is going to involve conversions and be less efficient. + + Basically, if you care about calendar operations and don't necessarily care + about time zones, then DateTime would be the type to use. However, if what + you care about is the system time, then SysTime is the type to use. + + $(D Clock.currTime()) will return the current time as a SysTime. If you want + to convert a SysTime to a Date or DateTime, simply cast it. And if you ever + want to convert a Date or DateTime to a SysTime, use SysTime's constructor, + and you can pass in the intended time zone with it (or don't pass in a + TimeZone, and the local time zone will be used). + + If you care about using time zones other than local time or UTC, you can use + PosixTimeZone on Posix systems (or on Windows, if you provide the TZ database + files), and you can use WindowsTimeZone on Windows systems. The time in SysTime + is kept internally in hnsecs from midnight, January 1st, 1 A.D. UTC. So, you + never get conversion errors when changing the time zone of a SysTime (since, + if the hnecs were kept in local time, DST would cause problems with conversions). + LocalTime is the TimeZone class which represents the local time, and UTC is the + TimeZone class which represents UTC. SysTime uses LocalTime if no TimeZone is + provided. For more details on time zones, look at the documentation for + TimeZone, PosixTimeZone, and WindowsTimeZone. + + SysTime's range is from approximately 29,000 B.C. to 29,000 A.D. + +/ +struct SysTime +{ +public: + + /++ + Params: + dateTime = The DateTime to use to set this SysTime's internal std + time. As DateTime has no concept of time zone, tz is used + as its time zone. + tz = The TimeZone to use for this SysTime. If null, LocalTime + will be used. The given DateTime is assumed to be in the + given time zone. + +/ + this(in DateTime dateTime, immutable TimeZone tz = null) nothrow + { + try + this(dateTime, FracSec.from!"hnsecs"(0), tz); + catch(Exception e) + assert(0, "FracSec's constructor threw when it shouldn't have."); + } + + unittest + { + assertPred!"=="(SysTime(DateTime.init, UTC())._stdTime, 0); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 30, 33), UTC())._stdTime, 450_330_000_000L); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 30, 33), UTC())._stdTime, -413_670_000_000L); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), UTC())._stdTime, 0); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 1), UTC())._stdTime, 10_000_000L); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC())._stdTime, -10_000_000L); + + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), new SimpleTimeZone(-60)).stdTime, 36_000_000_000L); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), new SimpleTimeZone(0)).stdTime, 0); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), new SimpleTimeZone(60)).stdTime, -36_000_000_000L); + } + + + /++ + Params: + dateTime = The DateTime to use to set this SysTime's internal std time. + As DateTime has no concept of time zone, tz is used as its + time zone. + fsec = The fractional seconds portion of the time. + tz = The TimeZone to use for this SysTime. If null, LocalTime will + be used. The given DateTime is assumed to be in the given + time zone. + +/ + this(in DateTime dateTime, in FracSec fsec, immutable TimeZone tz = null) nothrow + { + if(tz is null) + _timezone = LocalTime(); + else + _timezone = tz; + + try + { + immutable dateDiff = (dateTime.date - Date(1, 1, 1)).total!"hnsecs"; + immutable todDiff = (dateTime.timeOfDay - TimeOfDay(0, 0, 0)).total!"hnsecs"; + + immutable adjustedTime = dateDiff + todDiff + fsec.hnsecs; + immutable standardTime = _timezone.tzToUTC(adjustedTime); + + this(standardTime, _timezone.get); + } + catch(Exception e) + assert(0, "Date, TimeOfDay, or DateTime's constructor threw when it shouldn't have."); + } + + unittest + { + assertPred!"=="(SysTime(DateTime.init, FracSec.init, UTC())._stdTime, 0); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 30, 33), FracSec.init, UTC())._stdTime, 450_330_000_000L); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 30, 33), FracSec.init, UTC())._stdTime, -413_670_000_000L); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC())._stdTime, 10_000L); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC())._stdTime, -10_000L); + + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).stdTime, -1); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).stdTime, -9_999_999); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0), UTC()).stdTime, -10_000_000); + } + + + /++ + Params: + date = The Date to use to set this SysTime's internal std time. + As Date has no concept of time zone, tz is used as its + time zone. + tz = The TimeZone to use for this SysTime. If null, LocalTime will + be used. The given Date is assumed to be in the given time zone. + +/ + this(in Date date, immutable TimeZone tz = null) nothrow + { + if(tz is null) + _timezone = LocalTime(); + else + _timezone = tz; + + try + { + immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; + immutable standardTime = _timezone.tzToUTC(adjustedTime); + + this(standardTime, _timezone.get); + } + catch(Exception e) + assert(0, "Date's constructor through when it shouldn't have"); + } + + unittest + { + assertPred!"=="(SysTime(Date.init, UTC())._stdTime, 0); + assertPred!"=="(SysTime(Date(1, 1, 1), UTC())._stdTime, 0); + assertPred!"=="(SysTime(Date(1, 1, 2), UTC())._stdTime, 864000000000); + assertPred!"=="(SysTime(Date(0, 12, 31), UTC())._stdTime, -864000000000); + } + + + /++ + 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 TimeZone to use for this SysTime. If null, LocalTime will be used. + +/ + this(long stdTime, immutable TimeZone tz = null) pure nothrow + { + _stdTime = stdTime; + + if(tz is null) + _timezone = LocalTime(); + else + _timezone = tz; + } + + unittest + { + assertPred!"=="(SysTime(0)._stdTime, 0); + } + + + /++ + Params: + rhs = The SysTime to assign to this one. + +/ + ref SysTime opAssign(const ref SysTime rhs) pure nothrow + { + _stdTime = rhs._stdTime; + _timezone = rhs._timezone.get; + + return this; + } + + + /++ + Params: + rhs = The SysTime to assign to this one. + +/ + ref SysTime opAssign(SysTime rhs) pure nothrow + { + _stdTime = rhs._stdTime; + _timezone = rhs._timezone.get; + + return this; + } + + + /++ + Checks for equality between this SysTime and the given + SysTime. + + Note that the time zone is ignored. Only the internal + std times (which are in UTC) are compared. + +/ + bool opEquals(const ref SysTime rhs) const pure nothrow + { + return _stdTime == rhs._stdTime; + } + + unittest + { + auto a = SysTime(DateTime.init, UTC()); + auto b = SysTime(0, UTC()); + assert(a == b); + assertPred!"=="(a, b); + assertPred!"=="(SysTime(DateTime.init, UTC()), SysTime(0, UTC())); + assertPred!"=="(SysTime(Date.init, UTC()), SysTime(0, UTC())); + assertPred!"=="(SysTime(0), SysTime(0)); + + assertPred!"=="(SysTime(Date(1999, 1, 1)), SysTime(Date(1999, 1, 1))); + assertPred!"=="(SysTime(Date(1999, 1, 1)), SysTime(Date(1999, 1, 1))); + assertPred!"=="(SysTime(Date(1, 7, 1)), SysTime(Date(1, 7, 1))); + assertPred!"=="(SysTime(Date(1, 1, 6)), SysTime(Date(1, 1, 6))); + + assertPred!"=="(SysTime(Date(1999, 7, 1)), SysTime(Date(1999, 7, 1))); + assertPred!"=="(SysTime(Date(1999, 7, 6)), SysTime(Date(1999, 7, 6))); + + assertPred!"=="(SysTime(Date(1, 7, 6)), SysTime(Date(1, 7, 6))); + + assertPred!"=="(SysTime(DateTime(1999, 1, 1, 0, 0, 0)), SysTime(Date(1999, 1, 1))); + assertPred!"=="(SysTime(Date(1999, 1, 1)), SysTime(DateTime(1999, 1, 1, 0, 0, 0))); + + assertPred!"!="(SysTime(DateTime(1999, 1, 1, 11, 30, 20)), SysTime(Date(1999, 1, 1))); + assertPred!"!="(SysTime(Date(1999, 1, 1)), SysTime(DateTime(1999, 1, 1, 11, 30, 20))); + + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)))); + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)))); + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)))); + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)))); + + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)))); + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)))); + assertPred!"=="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)))); + + { + auto sysUTC = SysTime(Date(1999, 1, 1)); + sysUTC.timezone = UTC(); + + auto sysLocal = SysTime(Date(1999, 1, 1)); + sysUTC.timezone = LocalTime(); + + assertPred!"=="(sysUTC, sysLocal); + assertPred!"=="(sysLocal, sysUTC); + + assertPred!"!="(SysTime(Date(1999, 1, 1), UTC()), SysTime(Date(1999, 1, 1), LocalTime())); + assertPred!"!="(SysTime(Date(1999, 7, 1), LocalTime()), SysTime(Date(1999, 7, 1), UTC())); + } + + { + auto sysUTC = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + sysUTC.timezone = UTC(); + + auto sysSimple = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + sysSimple.timezone = new SimpleTimeZone(-360); + + assertPred!"=="(sysUTC, sysSimple); + assertPred!"=="(sysSimple, sysUTC); + + assertPred!"!="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), UTC()), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), sysSimple.timezone)); + assertPred!"!="(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), sysSimple.timezone), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), UTC())); + } + + { + auto sysUTC = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), UTC()); + auto sysSimple = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), new SimpleTimeZone(240)); + + assertPred!"!="(sysUTC, sysSimple); + assertPred!"!="(sysSimple, sysUTC); + + sysUTC = SysTime(DateTime(Date(1999, 7, 5), TimeOfDay(20, 0, 0)), UTC()); + + assertPred!"=="(sysUTC, sysSimple); + assertPred!"=="(sysSimple, sysUTC); + } + + 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)); + static assert(__traits(compiles, st == st)); + static assert(__traits(compiles, st == cst)); + //static assert(__traits(compiles, st == ist)); + static assert(__traits(compiles, cst == st)); + static assert(__traits(compiles, cst == cst)); + //static assert(__traits(compiles, cst == ist)); + //static assert(__traits(compiles, ist == st)); + //static assert(__traits(compiles, ist == cst)); + //static assert(__traits(compiles, ist == ist)); + } + + + /++ + Compares this SysTime with the given SysTime. + + Time zone is irrelevant to comparing SysTimes. + + Returns: + $(TABLE + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in SysTime rhs) const pure nothrow + { + if(_stdTime < rhs._stdTime) + return -1; + if(_stdTime > rhs._stdTime) + return 1; + + return 0; + } + + unittest + { + //Test A.D. + assertPred!("opCmp", "==")(SysTime(DateTime.init, UTC()), SysTime(0)); + assertPred!("opCmp", "==")(SysTime(Date.init, UTC()), SysTime(0)); + + assertPred!("opCmp", "==")(SysTime(Date(1999, 1, 1)), SysTime(Date(1999, 1, 1))); + assertPred!("opCmp", "==")(SysTime(Date(1999, 1, 1)), SysTime(Date(1999, 1, 1))); + assertPred!("opCmp", "==")(SysTime(Date(1, 7, 1)), SysTime(Date(1, 7, 1))); + assertPred!("opCmp", "==")(SysTime(Date(1, 1, 6)), SysTime(Date(1, 1, 6))); + + assertPred!("opCmp", "==")(SysTime(Date(1999, 7, 1)), SysTime(Date(1999, 7, 1))); + assertPred!("opCmp", "==")(SysTime(Date(1999, 7, 6)), SysTime(Date(1999, 7, 6))); + + assertPred!("opCmp", "==")(SysTime(Date(1, 7, 6)), SysTime(Date(1, 7, 6))); + + assertPred!("opCmp", ">")(SysTime(DateTime(1999, 1, 1, 11, 30, 20)), SysTime(Date(1999, 1, 1))); + assertPred!("opCmp", "<")(SysTime(Date(1999, 1, 1)), SysTime(DateTime(1999, 1, 1, 11, 30, 20))); + + assertPred!("opCmp", "<")(SysTime(Date(1999, 7, 6)), SysTime(Date(2000, 7, 6))); + assertPred!("opCmp", ">")(SysTime(Date(2000, 7, 6)), SysTime(Date(1999, 7, 6))); + assertPred!("opCmp", "<")(SysTime(Date(1999, 7, 6)), SysTime(Date(1999, 8, 6))); + assertPred!("opCmp", ">")(SysTime(Date(1999, 8, 6)), SysTime(Date(1999, 7, 6))); + assertPred!("opCmp", "<")(SysTime(Date(1999, 7, 6)), SysTime(Date(1999, 7, 7))); + assertPred!("opCmp", ">")(SysTime(Date(1999, 7, 7)), SysTime(Date(1999, 7, 6))); + + assertPred!("opCmp", "<")(SysTime(Date(1999, 8, 7)), SysTime(Date(2000, 7, 6))); + assertPred!("opCmp", ">")(SysTime(Date(2000, 8, 6)), SysTime(Date(1999, 7, 7))); + assertPred!("opCmp", "<")(SysTime(Date(1999, 7, 7)), SysTime(Date(2000, 7, 6))); + assertPred!("opCmp", ">")(SysTime(Date(2000, 7, 6)), SysTime(Date(1999, 7, 7))); + assertPred!("opCmp", "<")(SysTime(Date(1999, 7, 7)), SysTime(Date(1999, 8, 6))); + assertPred!("opCmp", ">")(SysTime(Date(1999, 8, 6)), SysTime(Date(1999, 7, 7))); + + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)))), + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)))); + + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)))); + + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))), + SysTime(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))), + SysTime(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))), + SysTime(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))), + SysTime(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))), + SysTime(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))), + SysTime(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))), + SysTime(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))), + SysTime(DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))), + SysTime(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)))); + + //Test B.C. + assertPred!("opCmp", "==")(SysTime(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "==")(SysTime(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "==")(SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "==")(SysTime(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)))); + + //Test Both + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)))); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33)))); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33))), + SysTime(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)))); + + { + auto sysUTC = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + sysUTC.timezone = UTC(); + + auto sysSimple = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + sysSimple.timezone = new SimpleTimeZone(-360); + + assertPred!("opCmp", "==")(sysUTC, sysSimple); + assertPred!("opCmp", "==")(sysSimple, sysUTC); + + assertPred!("opCmp", "<")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), UTC()), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), sysSimple.timezone)); + assertPred!("opCmp", ">")(SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), sysSimple.timezone), + SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), UTC())); + } + + { + auto sysUTC = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), UTC()); + auto sysSimple = SysTime(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), new SimpleTimeZone(240)); + + assertPred!("opCmp", ">")(sysUTC, sysSimple); + assertPred!("opCmp", "<")(sysSimple, sysUTC); + + sysUTC = SysTime(DateTime(Date(1999, 7, 5), TimeOfDay(20, 0, 0)), UTC()); + + assertPred!("opCmp", "==")(sysUTC, sysSimple); + assertPred!("opCmp", "==")(sysSimple, sysUTC); + } + + 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)); + static assert(__traits(compiles, st.opCmp(st))); + static assert(__traits(compiles, st.opCmp(cst))); + //static assert(__traits(compiles, st.opCmp(ist))); + static assert(__traits(compiles, cst.opCmp(st))); + static assert(__traits(compiles, cst.opCmp(cst))); + //static assert(__traits(compiles, cst.opCmp(ist))); + //static assert(__traits(compiles, ist.opCmp(st))); + //static assert(__traits(compiles, ist.opCmp(cst))); + //static assert(__traits(compiles, ist.opCmp(ist))); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() const nothrow + { + return (cast(Date)this).year; + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).year, 1); + assertPred!"=="(SysTime(1, UTC()).year, 1); + assertPred!"=="(SysTime(-1, UTC()).year, 0); + assertPred!"=="(SysTime(DateTime(12, 1, 1, 0, 0, 0)).year, 12); + assertPred!"=="(SysTime(DateTime(-12, 1, 1, 0, 0, 0)).year, -12); + assertPred!"=="(SysTime(Date(1999, 7, 6)).year, 1999); + assertPred!"=="(SysTime(Date(-1999, 7, 6)).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)); + //static assert(__traits(compiles, ist.year)); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this SysTime's year to. + + Throws: + DateTimeException if the new year is not a leap year and the resulting + date would be on February 29th. + + Examples: +-------------------- +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); +-------------------- + +/ + @property void year(int year) + { + 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; + } + + unittest + { + static void testST(SysTime st, int year, in SysTime expected, size_t line = __LINE__) + { + st.year = year; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1999, SysTime(Date(1999, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 0, SysTime(Date(0, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1999, SysTime(Date(-1999, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1999, SysTime(DateTime(1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), -1999, SysTime(DateTime(-1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 1999, SysTime(Date(1999, 1, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 0, SysTime(Date(0, 1, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1999, SysTime(Date(-1999, 1, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1999, SysTime(DateTime(1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), -1999, SysTime(DateTime(-1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(Date(12, 7, 2))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(Date(5, 7, 2))); + testST(SysTime(Date(0, 7, 2)), 999, SysTime(Date(999, 7, 2))); + testST(SysTime(Date(-1202, 7, 2)), 2300, SysTime(Date(2300, 7, 2))); + + testST(SysTime(Date(1, 7, 2)), -12, SysTime(Date(-12, 7, 2))); + testST(SysTime(Date(5007, 7, 2)), -5, SysTime(Date(-5, 7, 2))); + testST(SysTime(Date(0, 7, 2)), -999, SysTime(Date(-999, 7, 2))); + testST(SysTime(Date(-1202, 7, 2)), -2300, SysTime(Date(-2300, 7, 2))); + + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 12, SysTime(DateTime(12, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 12, SysTime(DateTime(12, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 0, SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), -1, SysTime(DateTime(-1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 1, SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 999, SysTime(DateTime(999, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2010, SysTime(DateTime(2010, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 5, SysTime(DateTime(5, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5, SysTime(DateTime(5, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 0, SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), -507, SysTime(DateTime(-507, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2300, SysTime(DateTime(2300, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5007, SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 12, SysTime(DateTime(12, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 12, SysTime(DateTime(12, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 0, SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), -1, SysTime(DateTime(-1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 1, SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 999, SysTime(DateTime(999, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2010, SysTime(DateTime(2010, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 5, SysTime(DateTime(5, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5, SysTime(DateTime(5, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 0, SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), -507, SysTime(DateTime(-507, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2300, SysTime(DateTime(2300, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5007, SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(12, 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.year = 7)); + //static assert(!__traits(compiles, ist.year = 7)); + + //Verify Examples. + 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); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + DateTimeException if $(D isAD) is true. + + Examples: +-------------------- +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); +-------------------- + +/ + @property ushort yearBC() const + { + return (cast(Date)this).yearBC; + } + + unittest + { + assertThrown!DateTimeException((in SysTime st){st.yearBC;}(SysTime(Date(1, 1, 1), UTC()))); + assertThrown!DateTimeException((in SysTime st){st.yearBC;}(SysTime(0, UTC()))); + assertThrown!DateTimeException((in SysTime st){st.yearBC;}(SysTime(1, UTC()))); + + assertPred!"=="(SysTime(-1, UTC()).yearBC, 1); + assertPred!"=="(SysTime(DateTime(-12, 1, 1, 0, 0, 0)).yearBC, 13); + + 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)); + static assert(__traits(compiles, st.year = 12)); + static assert(!__traits(compiles, cst.year = 12)); + //static assert(!__traits(compiles, ist.year = 12)); + + //Verify Examples. + 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); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this SysTime's year to. + + Throws: + DateTimeException if a non-positive value is given. + + Examples: +-------------------- +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))); +-------------------- + +/ + @property void yearBC(int year) + { + 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; + } + + unittest + { + static void testSTInvalid(SysTime st, int year) + { + st.yearBC = year; + } + + static void testST(SysTime st, int year, in SysTime expected, size_t line = __LINE__) + { + st.yearBC = year; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 1, 1)), -1)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 1, 1)), -1202)); + + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1999, SysTime(Date(-1998, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(Date(0, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1999, SysTime(DateTime(-1998, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(Date(-11, 7, 2))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(Date(-4, 7, 2))); + testST(SysTime(Date(0, 7, 2)), 999, SysTime(Date(-998, 7, 2))); + testST(SysTime(Date(-1202, 7, 2)), 2300, SysTime(Date(-2299, 7, 2))); + + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 12, SysTime(DateTime(-11, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 12, SysTime(DateTime(-11, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 1, SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 999, SysTime(DateTime(-998, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(1, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2010, SysTime(DateTime(-2009, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 5, SysTime(DateTime(-4, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5, SysTime(DateTime(-4, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2300, SysTime(DateTime(-2299, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(5007, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5007, SysTime(DateTime(-5006, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 12, SysTime(DateTime(-11, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 12, SysTime(DateTime(-11, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 1, SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 999, SysTime(DateTime(-998, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(0, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2010, SysTime(DateTime(-2009, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0)), 5, SysTime(DateTime(-4, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5, SysTime(DateTime(-4, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 2300, SysTime(DateTime(-2299, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + testST(SysTime(DateTime(-1202, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007)), 5007, SysTime(DateTime(-5006, 7, 2, 1, 3, 2), FracSec.from!"hnsecs"(5007))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(-11, 1, 1, 0, 0, 0), UTC())); + + { + 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)); + static assert(__traits(compiles, st.yearBC = 12)); + static assert(!__traits(compiles, cst.yearBC = 12)); + //static assert(!__traits(compiles, ist.yearBC = 12)); + } + + //Verify Examples. + { + 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))); + } + } + + + /++ + Month of a Gregorian Year. + + Examples: +-------------------- +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); +-------------------- + +/ + @property Month month() const nothrow + { + return (cast(Date)this).month; + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).month, 1); + assertPred!"=="(SysTime(1, UTC()).month, 1); + assertPred!"=="(SysTime(-1, UTC()).month, 12); + assertPred!"=="(SysTime(DateTime(1, 12, 1, 0, 0, 0)).month, 12); + assertPred!"=="(SysTime(DateTime(0, 12, 1, 0, 0, 0)).month, 12); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)).month, 7); + assertPred!"=="(SysTime(DateTime(-1999, 7, 6, 12, 30, 33)).month, 7); + + 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)); + //static assert(__traits(compiles, ist.month)); + + //Verify Examples. + 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); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this SysTime's month to. + + Throws: + DateTimeException if the given month is not a valid month. + +/ + @property void month(Month month) + { + 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; + } + + unittest + { + static void testSTInvalid(SysTime st, Month month) + { + st.month = month; + } + + static void testST(SysTime st, Month month, in SysTime expected, size_t line = __LINE__) + { + st.month = month; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testSTInvalid(SysTime(DateTime(1, 1, 1, 12, 30, 33)), cast(Month)0)); + assertThrown!DateTimeException(testSTInvalid(SysTime(DateTime(1, 1, 1, 12, 30, 33)), cast(Month)13)); + assertThrown!DateTimeException(testSTInvalid(SysTime(DateTime(1, 1, 29, 12, 30, 33)), cast(Month)2)); + assertThrown!DateTimeException(testSTInvalid(SysTime(DateTime(1, 7, 31, 12, 30, 33)), cast(Month)6)); + assertThrown!DateTimeException(testSTInvalid(SysTime(DateTime(4, 7, 30, 12, 30, 33)), cast(Month)2)); + + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), cast(Month)1, SysTime(Date(1, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), cast(Month)7, SysTime(Date(1, 7, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), cast(Month)12, SysTime(Date(1, 12, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)7, SysTime(DateTime(1, 7, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)12, SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), cast(Month)1, SysTime(Date(0, 1, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), cast(Month)7, SysTime(Date(0, 7, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), cast(Month)12, SysTime(Date(0, 12, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)1, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)7, SysTime(DateTime(0, 7, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)12, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), cast(Month)12, SysTime(Date(1, 12, 2))); + testST(SysTime(Date(5007, 7, 2)), cast(Month)5, SysTime(Date(5007, 5, 2))); + testST(SysTime(Date(0, 7, 2)), cast(Month)7, SysTime(Date(0, 7, 2))); + testST(SysTime(Date(-1202, 7, 2)), cast(Month)1, SysTime(Date(-1202, 1, 2))); + + testST(SysTime(DateTime(1, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(0)), cast(Month)12, SysTime(DateTime(1, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(1)), cast(Month)12, SysTime(DateTime(1, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(2)), cast(Month)7, SysTime(DateTime(1, 7, 29, 9, 13, 2), FracSec.from!"hnsecs"(2))); + testST(SysTime(DateTime(1, 11, 28, 10, 13, 2), FracSec.from!"hnsecs"(3)), cast(Month)1, SysTime(DateTime(1, 1, 28, 10, 13, 2), FracSec.from!"hnsecs"(3))); + testST(SysTime(DateTime(1, 1, 28, 10, 13, 2), FracSec.from!"hnsecs"(4)), cast(Month)2, SysTime(DateTime(1, 2, 28, 10, 13, 2), FracSec.from!"hnsecs"(4))); + testST(SysTime(DateTime(1, 7, 28, 10, 13, 2), FracSec.from!"hnsecs"(5)),cast(Month) 6, SysTime(DateTime(1, 6, 28, 10, 13, 2), FracSec.from!"hnsecs"(5))); + testST(SysTime(DateTime(4, 7, 29, 10, 13, 2), FracSec.from!"hnsecs"(6)), cast(Month)2, SysTime(DateTime(4, 2, 29, 10, 13, 2), FracSec.from!"hnsecs"(6))); + testST(SysTime(DateTime(4, 7, 31, 10, 13, 2), FracSec.from!"hnsecs"(6)), cast(Month)8, SysTime(DateTime(4, 8, 31, 10, 13, 2), FracSec.from!"hnsecs"(6))); + + testST(SysTime(DateTime(1207, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(0)), cast(Month)12, SysTime(DateTime(1207, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1207, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(11)), cast(Month)12, SysTime(DateTime(1207, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(11))); + testST(SysTime(DateTime(1207, 12, 29, 9, 12, 3), FracSec.from!"hnsecs"(10)), cast(Month)7, SysTime(DateTime(1207, 7, 29, 9, 12, 3), FracSec.from!"hnsecs"(10))); + testST(SysTime(DateTime(1207, 11, 28, 10, 12, 3), FracSec.from!"hnsecs"(9)), cast(Month)1, SysTime(DateTime(1207, 1, 28, 10, 12, 3), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(1207, 1, 28, 10, 12, 3), FracSec.from!"hnsecs"(8)), cast(Month)2, SysTime(DateTime(1207, 2, 28, 10, 12, 3), FracSec.from!"hnsecs"(8))); + testST(SysTime(DateTime(1207, 7, 28, 10, 12, 3), FracSec.from!"hnsecs"(7)), cast(Month)6, SysTime(DateTime(1207, 6, 28, 10, 12, 3), FracSec.from!"hnsecs"(7))); + + testST(SysTime(DateTime(0, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(0)), cast(Month)12, SysTime(DateTime(0, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(1)), cast(Month)12, SysTime(DateTime(0, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 29, 9, 13, 2), FracSec.from!"hnsecs"(2)), cast(Month)7, SysTime(DateTime(0, 7, 29, 9, 13, 2), FracSec.from!"hnsecs"(2))); + testST(SysTime(DateTime(0, 11, 28, 10, 13, 2), FracSec.from!"hnsecs"(3)), cast(Month)1, SysTime(DateTime(0, 1, 28, 10, 13, 2), FracSec.from!"hnsecs"(3))); + testST(SysTime(DateTime(0, 1, 28, 10, 13, 2), FracSec.from!"hnsecs"(4)), cast(Month)2, SysTime(DateTime(0, 2, 28, 10, 13, 2), FracSec.from!"hnsecs"(4))); + testST(SysTime(DateTime(0, 7, 28, 10, 13, 2), FracSec.from!"hnsecs"(5)), cast(Month)6, SysTime(DateTime(0, 6, 28, 10, 13, 2), FracSec.from!"hnsecs"(5))); + + testST(SysTime(DateTime(0, 12, 30, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)11, SysTime(DateTime(0, 11, 30, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(-1, 7, 28, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)2, SysTime(DateTime(-1, 2, 28, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 7, 29, 0, 0, 0), FracSec.from!"hnsecs"(1)), cast(Month)2, SysTime(DateTime(0, 2, 29, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 7, 29, 10, 13, 2), FracSec.from!"hnsecs"(6)), cast(Month)2, SysTime(DateTime(0, 2, 29, 10, 13, 2), FracSec.from!"hnsecs"(6))); + testST(SysTime(DateTime(0, 7, 31, 10, 13, 2), FracSec.from!"hnsecs"(6)), cast(Month)8, SysTime(DateTime(0, 8, 31, 10, 13, 2), FracSec.from!"hnsecs"(6))); + + testST(SysTime(DateTime(-9999, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(0)), cast(Month)12, SysTime(DateTime(-9999, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(-9999, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(11)), cast(Month)12, SysTime(DateTime(-9999, 12, 29, 9, 12, 2), FracSec.from!"hnsecs"(11))); + testST(SysTime(DateTime(-9999, 12, 29, 9, 12, 3), FracSec.from!"hnsecs"(10)), cast(Month)7, SysTime(DateTime(-9999, 7, 29, 9, 12, 3), FracSec.from!"hnsecs"(10))); + testST(SysTime(DateTime(-9999, 11, 28, 10, 12, 3), FracSec.from!"hnsecs"(9)), cast(Month)1, SysTime(DateTime(-9999, 1, 28, 10, 12, 3), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(-9999, 1, 28, 10, 12, 3), FracSec.from!"hnsecs"(8)), cast(Month)2, SysTime(DateTime(-9999, 2, 28, 10, 12, 3), FracSec.from!"hnsecs"(8))); + testST(SysTime(DateTime(-9999, 7, 28, 10, 12, 3), FracSec.from!"hnsecs"(7)), cast(Month)6, SysTime(DateTime(-9999, 6, 28, 10, 12, 3), FracSec.from!"hnsecs"(7))); + + testST(SysTime(0, UTC()), cast(Month)12, SysTime(DateTime(1, 12, 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.month = 12)); + //static assert(!__traits(compiles, ist.month = 12)); + } + + + /++ + Day of a Gregorian Month. + + Examples: +-------------------- +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); +-------------------- + +/ + @property ubyte day() const nothrow + { + return (cast(Date)this).day; + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).day, 1); + assertPred!"=="(SysTime(1, UTC()).day, 1); + assertPred!"=="(SysTime(-1, UTC()).day, 31); + assertPred!"=="(SysTime(DateTime(50, 2, 4)).day, 4); + assertPred!"=="(SysTime(DateTime(50, 2, 4, 0, 0, 1)).day, 4); + assertPred!"=="(SysTime(DateTime(1999, 7, 6)).day, 6); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 0, 0, 0)).day, 6); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)).day, 6); + assertPred!"=="(SysTime(DateTime(-50, 2, 4)).day, 4); + assertPred!"=="(SysTime(DateTime(-50, 2, 4, 0, 0, 1)).day, 4); + assertPred!"=="(SysTime(Date(-1999, 7, 6)).day, 6); + assertPred!"=="(SysTime(DateTime(-1999, 7, 6, 0, 0, 0)).day, 6); + assertPred!"=="(SysTime(DateTime(-1999, 7, 6, 12, 30, 33)).day, 6); + + assertPred!"=="(SysTime(DateTime(1, 1, 12, 0, 0, 0)).day, 12); + assertPred!"=="(SysTime(DateTime(0, 1, 12, 0, 0, 0)).day, 12); + + 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)); + //static assert(__traits(compiles, ist.day)); + + //Verify Examples. + 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); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this SysTime's day to. + + Throws: + DateTimeException if the given day is not a valid day of the current + month. + +/ + @property void day(int day) + { + 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; + } + + unittest + { + static void testSTInvalid(SysTime st, int day) + { + st.day = day; + } + + static void testST(SysTime st, int day, in SysTime expected, size_t line = __LINE__) + { + st.day = day; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + //Test A.D. + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 1, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 2, 1)), 29)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(4, 2, 1)), 30)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 3, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 4, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 5, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 6, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 7, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 8, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 9, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 10, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 11, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(4, 2, 1)), 29)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(1, 12, 1)), 31)); + + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(Date(1, 1, 1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 7, SysTime(Date(1, 1, 7))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 12, SysTime(Date(1, 1, 12))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 1, 7, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 1, 12, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(Date(1, 7, 12))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(Date(5007, 7, 5))); + + testST(SysTime(DateTime(1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(1, 1, 6, 12, 22, 17), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(1, 2, 5, 12, 22, 17), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 29, SysTime(DateTime(1, 12, 29, 12, 22, 17), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(999, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(999, 1, 6, 12, 22, 17), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(999, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(999, 2, 5, 12, 22, 17), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(999, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 29, SysTime(DateTime(999, 12, 29, 12, 22, 17), FracSec.from!"hnsecs"(900_000))); + + //Test B.C. + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 1, 1)), 0)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 1, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 2, 1)), 29)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(0, 2, 1)), 30)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 3, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 4, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 5, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 6, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 7, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 8, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 9, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 10, 1)), 32)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 11, 1)), 31)); + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(0, 2, 1)), 29)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testSTInvalid(SysTime(Date(-1, 12, 1)), 31)); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 1, SysTime(Date(0, 1, 1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 7, SysTime(Date(0, 1, 7))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 12, SysTime(Date(0, 1, 12))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 1, 7, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 1, 12, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(0, 7, 2)), 7, SysTime(Date(0, 7, 7))); + testST(SysTime(Date(-1202, 7, 2)), 1, SysTime(Date(-1202, 7, 1))); + + testST(SysTime(DateTime(0, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(0, 1, 6, 12, 22, 17), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(0, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(0, 2, 5, 12, 22, 17), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(0, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 29, SysTime(DateTime(0, 12, 29, 12, 22, 17), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(-1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(-1, 1, 6, 12, 22, 17), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(-1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(-1, 2, 5, 12, 22, 17), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(-1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 29, SysTime(DateTime(-1, 12, 29, 12, 22, 17), FracSec.from!"hnsecs"(900_000))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(1, 1, 12, 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.day = 27)); + //static assert(!__traits(compiles, ist.day = 27)); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + return cast(ubyte)getUnitsFromHNSecs!"hours"(hnsecs); + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).hour, 0); + assertPred!"=="(SysTime(1, UTC()).hour, 0); + assertPred!"=="(SysTime(-1, UTC()).hour, 23); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 0, 0)).hour, 12); + assertPred!"=="(SysTime(DateTime(0, 1, 1, 12, 0, 0)).hour, 12); + + 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)); + //static assert(__traits(compiles, ist.hour)); + } + + + /++ + Hours past midnight. + + Params: + hour = The hours to set this SysTime's hour to. + + Throws: + DateTimeException if the given hour are not a valid hour of the day. + +/ + @property void hour(int hour) + { + 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; + } + + unittest + { + static void testSTInvalid(SysTime st, int hour) + { + st.hour = hour; + } + + static void testST(SysTime st, int hour, in SysTime expected, size_t line = __LINE__) + { + st.hour = hour; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1999, 7, 6)), 24)); + + //Test A.D. + testST(SysTime(Date(1, 1, 1)), 1, SysTime(DateTime(1, 1, 1, 1, 0, 0))); + testST(SysTime(Date(1, 1, 1)), 7, SysTime(DateTime(1, 1, 1, 7, 0, 0))); + testST(SysTime(Date(1, 1, 1)), 12, SysTime(DateTime(1, 1, 1, 12, 0, 0))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 1, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 1, 1, 7, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 1, 1, 12, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(1, 12, 31, 1, 59, 59))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(1, 12, 31, 7, 59, 59))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(1, 12, 31, 12, 59, 59))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 12, 31, 1, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 12, 31, 7, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 12, 31, 12, 59, 59), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(DateTime(1, 7, 2, 12, 0, 0))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(DateTime(5007, 7, 2, 5, 0, 0))); + + testST(SysTime(DateTime(1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(1, 1, 7, 6, 22, 17), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(1, 1, 1, 1, 22, 17), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(1, 2, 1, 1, 22, 17), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(1, 2, 27, 5, 22, 17), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(1, 12, 1, 1, 22, 17), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(1, 12, 31, 23, 22, 17), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(999, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(999, 1, 7, 6, 22, 17), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(999, 1, 1, 1, 22, 17), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(999, 2, 1, 1, 22, 17), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(999, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(999, 2, 27, 5, 22, 17), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(999, 12, 1, 1, 22, 17), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(999, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(999, 12, 31, 23, 22, 17), FracSec.from!"hnsecs"(900_000))); + + //Test B.C. + testST(SysTime(Date(0, 1, 1)), 1, SysTime(DateTime(0, 1, 1, 1, 0, 0))); + testST(SysTime(Date(0, 1, 1)), 7, SysTime(DateTime(0, 1, 1, 7, 0, 0))); + testST(SysTime(Date(0, 1, 1)), 12, SysTime(DateTime(0, 1, 1, 12, 0, 0))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 1, 1, 1, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 1, 1, 7, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 1, 1, 12, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(0, 12, 31, 1, 59, 59))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(0, 12, 31, 7, 59, 59))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(0, 12, 31, 12, 59, 59))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 12, 31, 1, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 12, 31, 7, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 12, 31, 12, 59, 59), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(0, 7, 2)), 7, SysTime(DateTime(0, 7, 2, 7, 0, 0))); + testST(SysTime(Date(-1202, 7, 2)), 1, SysTime(DateTime(-1202, 7, 2, 1, 0, 0))); + + testST(SysTime(DateTime(0, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(0, 1, 7, 6, 22, 17), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(0, 1, 1, 1, 22, 17), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(0, 2, 1, 1, 22, 17), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(0, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(0, 2, 27, 5, 22, 17), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(0, 12, 1, 1, 22, 17), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(0, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(0, 12, 31, 23, 22, 17), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(-1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(-1, 1, 7, 6, 22, 17), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(-1, 1, 1, 1, 22, 17), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(-1, 2, 1, 1, 22, 17), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(-1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(-1, 2, 27, 5, 22, 17), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(-1, 12, 1, 1, 22, 17), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(-1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(-1, 12, 31, 23, 22, 17), FracSec.from!"hnsecs"(900_000))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(1, 1, 1, 12, 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.hour = 27)); + //static assert(!__traits(compiles, ist.hour = 27)); + } + + + /++ + Minutes past the current hour. + +/ + @property ubyte minute() const nothrow + { + 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); + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).minute, 0); + assertPred!"=="(SysTime(1, UTC()).minute, 0); + assertPred!"=="(SysTime(-1, UTC()).minute, 59); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 30, 0)).minute, 30); + assertPred!"=="(SysTime(DateTime(0, 1, 1, 0, 30, 0)).minute, 30); + + 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)); + //static assert(__traits(compiles, ist.minute)); + } + + + /++ + Minutes past the current hour. + + Params: + minutes = The minute to set this SysTime's minute to. + + Throws: + DateTimeException if the given minute are not a valid minute of an + hour. + +/ + @property void minute(int minute) + { + 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; + } + + unittest + { + static void testSTInvalid(SysTime st, int minute) + { + st.minute = minute; + } + + static void testST(SysTime st, int minute, in SysTime expected, size_t line = __LINE__) + { + st.minute = minute; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1999, 7, 6)), 60)); + + //Test A.D. + testST(SysTime(Date(1, 1, 1)), 1, SysTime(DateTime(1, 1, 1, 0, 1, 0))); + testST(SysTime(Date(1, 1, 1)), 7, SysTime(DateTime(1, 1, 1, 0, 7, 0))); + testST(SysTime(Date(1, 1, 1)), 12, SysTime(DateTime(1, 1, 1, 0, 12, 0))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 1, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 1, 1, 0, 7, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 1, 1, 0, 12, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(1, 12, 31, 23, 1, 59))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(1, 12, 31, 23, 7, 59))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(1, 12, 31, 23, 12, 59))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 12, 31, 23, 1, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 12, 31, 23, 7, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 12, 31, 23, 12, 59), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(DateTime(1, 7, 2, 0, 12, 0))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(DateTime(5007, 7, 2, 0, 5, 0))); + + testST(SysTime(DateTime(1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(1, 1, 7, 12, 6, 17), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(1, 1, 1, 12, 1, 17), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(1, 2, 1, 12, 1, 17), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(1, 2, 27, 12, 5, 17), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(1, 12, 1, 12, 1, 17), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(1, 12, 31, 12, 23, 17), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(999, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(999, 1, 7, 12, 6, 17), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(999, 1, 1, 12, 1, 17), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(999, 2, 1, 12, 1, 17), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(999, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(999, 2, 27, 12, 5, 17), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(999, 12, 1, 12, 1, 17), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(999, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(999, 12, 31, 12, 23, 17), FracSec.from!"hnsecs"(900_000))); + + //Test B.C. + testST(SysTime(Date(0, 1, 1)), 1, SysTime(DateTime(0, 1, 1, 0, 1, 0))); + testST(SysTime(Date(0, 1, 1)), 7, SysTime(DateTime(0, 1, 1, 0, 7, 0))); + testST(SysTime(Date(0, 1, 1)), 12, SysTime(DateTime(0, 1, 1, 0, 12, 0))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 1, 1, 0, 1, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 1, 1, 0, 7, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 1, 1, 0, 12, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(0, 12, 31, 23, 1, 59))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(0, 12, 31, 23, 7, 59))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(0, 12, 31, 23, 12, 59))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 12, 31, 23, 1, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 12, 31, 23, 7, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 12, 31, 23, 12, 59), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(0, 7, 2)), 7, SysTime(DateTime(0, 7, 2, 0, 7, 0))); + testST(SysTime(Date(-1202, 7, 2)), 1, SysTime(DateTime(-1202, 7, 2, 0, 1, 0))); + + testST(SysTime(DateTime(0, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(0, 1, 7, 12, 6, 17), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(0, 1, 1, 12, 1, 17), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(0, 2, 1, 12, 1, 17), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(0, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(0, 2, 27, 12, 5, 17), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(0, 12, 1, 12, 1, 17), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(0, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(0, 12, 31, 12, 23, 17), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(-1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(-1, 1, 7, 12, 6, 17), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(-1, 1, 1, 12, 1, 17), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(-1, 2, 1, 12, 1, 17), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(-1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(-1, 2, 27, 12, 5, 17), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(-1, 12, 1, 12, 1, 17), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(-1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(-1, 12, 31, 12, 23, 17), FracSec.from!"hnsecs"(900_000))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(1, 1, 1, 0, 12, 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.minute = 27)); + //static assert(!__traits(compiles, ist.minute = 27)); + } + + + /++ + Seconds past the current minute. + +/ + @property ubyte second() const nothrow + { + 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); + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).second, 0); + assertPred!"=="(SysTime(1, UTC()).second, 0); + assertPred!"=="(SysTime(-1, UTC()).second, 59); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 33)).second, 33); + assertPred!"=="(SysTime(DateTime(0, 1, 1, 0, 0, 33)).second, 33); + + 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.second)); + //static assert(__traits(compiles, ist.second)); + } + + + /++ + Seconds past the current minute. + + Params + second = The second to set this SysTime's second to. + + Throws: + DateTimeException if the given second are not a valid second of a minute. + +/ + @property void second(int second) + { + 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; + } + + unittest + { + static void testSTInvalid(SysTime st, int second) + { + st.second = second; + } + + static void testST(SysTime st, int second, in SysTime expected, size_t line = __LINE__) + { + st.second = second; + assertPred!"=="(st, expected, "", __FILE__, line); + } + + assertThrown!DateTimeException(testSTInvalid(SysTime(Date(1999, 7, 6)), 60)); + + //Test A.D. + testST(SysTime(Date(1, 1, 1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 1))); + testST(SysTime(Date(1, 1, 1)), 7, SysTime(DateTime(1, 1, 1, 0, 0, 7))); + testST(SysTime(Date(1, 1, 1)), 12, SysTime(DateTime(1, 1, 1, 0, 0, 12))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 1, 1, 0, 0, 7), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 1, 1, 0, 0, 12), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(1, 12, 31, 23, 59, 1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(1, 12, 31, 23, 59, 7))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(1, 12, 31, 23, 59, 12))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 12, 31, 23, 59, 1), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 12, 31, 23, 59, 7), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 12, 31, 23, 59, 12), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(DateTime(1, 7, 2, 0, 0, 12))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(DateTime(5007, 7, 2, 0, 0, 5))); + + testST(SysTime(DateTime(1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(1, 1, 7, 12, 22, 6), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(1, 1, 1, 12, 22, 1), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(1, 2, 1, 12, 22, 1), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(1, 2, 27, 12, 22, 5), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(1, 12, 1, 12, 22, 1), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(1, 12, 31, 12, 22, 23), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(999, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(999, 1, 7, 12, 22, 6), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(999, 1, 1, 12, 22, 1), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(999, 2, 1, 12, 22, 1), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(999, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(999, 2, 27, 12, 22, 5), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(999, 12, 1, 12, 22, 1), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(999, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(999, 12, 31, 12, 22, 23), FracSec.from!"hnsecs"(900_000))); + + //Test B.C. + testST(SysTime(Date(0, 1, 1)), 1, SysTime(DateTime(0, 1, 1, 0, 0, 1))); + testST(SysTime(Date(0, 1, 1)), 7, SysTime(DateTime(0, 1, 1, 0, 0, 7))); + testST(SysTime(Date(0, 1, 1)), 12, SysTime(DateTime(0, 1, 1, 0, 0, 12))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 1, 1, 0, 0, 7), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 1, 1, 0, 0, 12), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(0, 12, 31, 23, 59, 7))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(0, 12, 31, 23, 59, 12))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 1), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 12, 31, 23, 59, 7), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 12, 31, 23, 59, 12), FracSec.from!"hnsecs"(1))); + + testST(SysTime(Date(0, 7, 2)), 7, SysTime(DateTime(0, 7, 2, 0, 0, 7))); + testST(SysTime(Date(-1202, 7, 2)), 1, SysTime(DateTime(-1202, 7, 2, 0, 0, 1))); + + testST(SysTime(DateTime(0, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(0, 1, 7, 12, 22, 6), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(0, 1, 1, 12, 22, 1), FracSec.from!"hnsecs"(70))); + testST(SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(0, 2, 1, 12, 22, 1), FracSec.from!"hnsecs"(700))); + testST(SysTime(DateTime(0, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(0, 2, 27, 12, 22, 5), FracSec.from!"hnsecs"(7000))); + testST(SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(0, 12, 1, 12, 22, 1), FracSec.from!"hnsecs"(70_000))); + testST(SysTime(DateTime(0, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(0, 12, 31, 12, 22, 23), FracSec.from!"hnsecs"(700_000))); + + testST(SysTime(DateTime(-1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(-1, 1, 7, 12, 22, 6), FracSec.from!"hnsecs"(9))); + testST(SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(-1, 1, 1, 12, 22, 1), FracSec.from!"hnsecs"(90))); + testST(SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(-1, 2, 1, 12, 22, 1), FracSec.from!"hnsecs"(900))); + testST(SysTime(DateTime(-1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(-1, 2, 27, 12, 22, 5), FracSec.from!"hnsecs"(9000))); + testST(SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(-1, 12, 1, 12, 22, 1), FracSec.from!"hnsecs"(90_000))); + testST(SysTime(DateTime(-1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(-1, 12, 31, 12, 22, 23), FracSec.from!"hnsecs"(900_000))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(1, 1, 1, 0, 0, 12), 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.seconds = 27)); + //static assert(!__traits(compiles, ist.seconds = 27)); + } + + + /++ + Fractional seconds passed the second. + +/ + @property FracSec fracSec() const nothrow + { + try + { + auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); + + if(hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); + + return FracSec.from!"hnsecs"(cast(int)hnsecs); + } + catch(Exception e) + assert(0, "FracSec.from!\"hnsecs\"() threw."); + } + + unittest + { + assertPred!"=="(SysTime(0, UTC()).fracSec.hnsecs, 0); + assertPred!"=="(SysTime(1, UTC()).fracSec.hnsecs, 1); + assertPred!"=="(SysTime(-1, UTC()).fracSec.hnsecs, 9_999_999); + + assertPred!"=="(SysTime(DateTime(200, 3, 30, 1, 27, 19), FracSec.from!"hnsecs"(348), UTC()).fracSec.hnsecs, 348); + assertPred!"=="(SysTime(DateTime(-1, 2, 17, 4, 5, 22), FracSec.from!"hnsecs"(347), UTC()).fracSec.hnsecs, 347); + + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 33), FracSec.from!"hnsecs"(502)).fracSec, FracSec.from!"hnsecs"(502)); + assertPred!"=="(SysTime(DateTime(0, 1, 1, 0, 0, 33), FracSec.from!"hnsecs"(502)).fracSec, FracSec.from!"hnsecs"(502)); + + 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.fracSec)); + //static assert(__traits(compiles, ist.fracSec)); + } + + + /++ + Fractional seconds passed the second. + + Params + fracSec = The fractional seconds to set this SysTimes's fractional + seconds to. + +/ + @property void fracSec(FracSec fracSec) nothrow + { + 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); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + hnsecs = fracSec.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; + } + + unittest + { + static void testST(SysTime st, int hnsecs, in SysTime expected, size_t line = __LINE__) + { + st.fracSec = FracSec.from!"hnsecs"(hnsecs); + assertPred!"=="(st, expected, "", __FILE__, line); + } + + //Test A.D. + testST(SysTime(Date(1, 1, 1)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(Date(1, 1, 1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(Date(1, 1, 1)), 7, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7))); + testST(SysTime(Date(1, 1, 1)), 12, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(12))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(12))); + + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 0, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(12))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(12))); + + testST(SysTime(Date(1, 7, 2)), 12, SysTime(DateTime(1, 7, 2, 0, 0, 0), FracSec.from!"hnsecs"(12))); + testST(SysTime(Date(5007, 7, 2)), 5, SysTime(DateTime(5007, 7, 2, 0, 0, 0), FracSec.from!"hnsecs"(5))); + + testST(SysTime(DateTime(1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(6))); + testST(SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(5))); + testST(SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(23))); + + testST(SysTime(DateTime(999, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(999, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(6))); + testST(SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(999, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(999, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(999, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(999, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(5))); + testST(SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(999, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(999, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(999, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(23))); + + //Test B.C. + testST(SysTime(Date(0, 1, 1)), 0, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(Date(0, 1, 1)), 1, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(Date(0, 1, 1)), 7, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7))); + testST(SysTime(Date(0, 1, 1)), 12, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(12))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(12))); + + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 7, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59)), 12, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(12))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 7, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1)), 12, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(12))); + + testST(SysTime(Date(0, 7, 2)), 7, SysTime(DateTime(0, 7, 2, 0, 0, 0), FracSec.from!"hnsecs"(7))); + testST(SysTime(Date(-1202, 7, 2)), 1, SysTime(DateTime(-1202, 7, 2, 0, 0, 0), FracSec.from!"hnsecs"(1))); + + testST(SysTime(DateTime(0, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(7)), 6, SysTime(DateTime(0, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(6))); + testST(SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(70)), 1, SysTime(DateTime(0, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(700)), 1, SysTime(DateTime(0, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(7000)), 5, SysTime(DateTime(0, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(5))); + testST(SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(70_000)), 1, SysTime(DateTime(0, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(700_000)), 23, SysTime(DateTime(0, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(23))); + + testST(SysTime(DateTime(-1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(9)), 6, SysTime(DateTime(-1, 1, 7, 12, 22, 17), FracSec.from!"hnsecs"(6))); + testST(SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(90)), 1, SysTime(DateTime(-1, 1, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(900)), 1, SysTime(DateTime(-1, 2, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(-1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(9000)), 5, SysTime(DateTime(-1, 2, 27, 12, 22, 17), FracSec.from!"hnsecs"(5))); + testST(SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(90_000)), 1, SysTime(DateTime(-1, 12, 1, 12, 22, 17), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(-1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(900_000)), 23, SysTime(DateTime(-1, 12, 31, 12, 22, 17), FracSec.from!"hnsecs"(23))); + + testST(SysTime(0, UTC()), 12, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(12), 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.fracSec = FracSec.from!"msecs"(7))); + //static assert(!__traits(compiles, ist.fracSec = FracSec.from!"msecs"(7))); + } + + + /++ + The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the internal + representation of SysTime. + +/ + @property long stdTime() const pure nothrow + { + return _stdTime; + } + + unittest + { + assertPred!"=="(SysTime(0).stdTime, 0); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 33), FracSec.from!"hnsecs"(502), UTC()).stdTime, 330000502L); + assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime, 621355968000000000L); + + 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)); + //static assert(__traits(compiles, ist.stdTime)); + } + + + /++ + The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the internal + representation of SysTime. + + Params + stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. + +/ + @property void stdTime(long stdTime) pure nothrow + { + _stdTime = stdTime; + } + + unittest + { + { + auto st = SysTime(0, UTC()); + st.stdTime = 330000502L; + assertPred!"=="(st, SysTime(DateTime(1, 1, 1, 0, 0, 33), FracSec.from!"hnsecs"(502), UTC())); + } + + { + auto st = SysTime(0, UTC()); + st.stdTime = 621355968000000000L; + assertPred!"=="(st, 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)); + } + + + /++ + The current time zone of this 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 SysTime's time zone before returning. + +/ + @property immutable(TimeZone) timezone() const pure nothrow + { + return _timezone.get; + } + + + /++ + The current time zone of this 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 SysTime's time zone before returning. + +/ + @property void timezone(immutable TimeZone timezone) pure nothrow + { + _timezone = timezone; + } + + + /++ + Returns whether DST is in effect for this SysTime. + +/ + @property bool dstInEffect() const nothrow + { + return _timezone.dstInEffect(_stdTime); + //This function's unit testing is done in the time zone classes. + } + + + /++ + Returns a SysTime with the same std time as this one, but with LocalTime + as its time zone. + +/ + SysTime toLocalTime() const nothrow + { + return SysTime(_stdTime, LocalTime()); + } + + unittest + { + { + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); + assertPred!"=="(sysTime, sysTime.toLocalTime()); + assertPred!"=="(sysTime._stdTime, sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone is sysTime.timezone); + assert(sysTime.toLocalTime().timezone !is UTC()); + } + + { + immutable stz = new SimpleTimeZone(-3 * 60); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27), stz); + assertPred!"=="(sysTime, sysTime.toLocalTime()); + assertPred!"=="(sysTime._stdTime, sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone !is UTC()); + assert(sysTime.toLocalTime().timezone !is stz); + } + } + + + /++ + Returns a SysTime with the same std time as this one, but with UTC as + its time zone. + +/ + SysTime toUTC() const pure nothrow + { + return SysTime(_stdTime, UTC()); + } + + unittest + { + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); + assertPred!"=="(sysTime, sysTime.toUTC()); + assertPred!"=="(sysTime._stdTime, sysTime.toUTC()._stdTime); + assert(sysTime.toUTC().timezone is UTC()); + assert(sysTime.toUTC().timezone !is LocalTime()); + assert(sysTime.toUTC().timezone !is sysTime.timezone); + } + + + /++ + Returns a SysTime with the same std time as this one, but with given + time zone as its time zone. + +/ + SysTime toOtherTZ(immutable TimeZone tz) const pure nothrow + { + return SysTime(_stdTime, tz); + } + + unittest + { + immutable stz = new SimpleTimeZone(11 * 60); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); + assertPred!"=="(sysTime, sysTime.toOtherTZ(stz)); + assertPred!"=="(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()); + } + + + /++ + Returns a time_t which represents this SysTime. + + If time_t is 32 bits, rather than 64, and the result can't fit in a 32-bit value, then the + closest value that can be held in 32 bits will be used (so time_t.max if it goes over or time_t.min + if it goes under). + +/ + time_t toUnixTime() const pure nothrow + { + return stdTimeToUnixTime(_stdTime); + } + + unittest + { + assertPred!"=="(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"usecs"(1), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime, 1); + assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999_999), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC()).toUnixTime, 0); + assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime, -1); + } + + + /++ + Returns a tm which represents this SysTime. + +/ + tm toTM() const nothrow + { + try + { + 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) + { + char[] zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName).dup; + zone ~= "\0"; + + timeInfo.tm_gmtoff = cast(int)convert!("hnsecs", "seconds")(adjTime - _stdTime); + timeInfo.tm_zone = zone.ptr; + } + + return timeInfo; + } + catch(Exception e) + assert(0, "Either DateTime's constructor threw."); + } + + unittest + { + version(Posix) + { + scope(exit) clearTZEnvVar(); + setTZEnvVar("America/Los_Angeles"); + } + + { + auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); + + assertPred!"=="(timeInfo.tm_sec, 0); + assertPred!"=="(timeInfo.tm_min, 0); + assertPred!"=="(timeInfo.tm_hour, 0); + assertPred!"=="(timeInfo.tm_mday, 1); + assertPred!"=="(timeInfo.tm_mon, 0); + assertPred!"=="(timeInfo.tm_year, 70); + assertPred!"=="(timeInfo.tm_wday, 4); + assertPred!"=="(timeInfo.tm_yday, 0); + + version(Posix) + assertPred!"=="(timeInfo.tm_isdst, 0); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) + { + assertPred!"=="(timeInfo.tm_gmtoff, -8 * 60 * 60); + assertPred!"=="(to!string(timeInfo.tm_zone), "PST"); + } + } + + { + auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), FracSec.from!"hnsecs"(15)).toTM(); + + assertPred!"=="(timeInfo.tm_sec, 7); + assertPred!"=="(timeInfo.tm_min, 15); + assertPred!"=="(timeInfo.tm_hour, 12); + assertPred!"=="(timeInfo.tm_mday, 4); + assertPred!"=="(timeInfo.tm_mon, 6); + assertPred!"=="(timeInfo.tm_year, 110); + assertPred!"=="(timeInfo.tm_wday, 0); + assertPred!"=="(timeInfo.tm_yday, 184); + + version(Posix) + assertPred!"=="(timeInfo.tm_isdst, 1); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) + { + assertPred!"=="(timeInfo.tm_gmtoff, -7 * 60 * 60); + assertPred!"=="(to!string(timeInfo.tm_zone), "PDT"); + } + } + } + + + /++ + Adds the given number of years or months to this 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 days 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 SysTime. + allowOverflow = Whether the days should be allowed to overflow, causing + the month to increment. + + Examples: +-------------------- +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))); +-------------------- + +/ + ref SysTime add(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) nothrow + 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(years, 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 add!"years"() with AllowDayOverlow.yes + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7); + assertPred!"=="(sysTime, SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9); + assertPred!"=="(sysTime, SysTime(Date(1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(7); + assertPred!"=="(sysTime, SysTime(DateTime(2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(-9); + assertPred!"=="(sysTime, SysTime(DateTime(1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(DateTime(2000, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); + } + + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 3, 1, 0, 7, 2), FracSec.from!"usecs"(1207))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7); + assertPred!"=="(sysTime, SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9); + assertPred!"=="(sysTime, SysTime(Date(-1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(-7); + assertPred!"=="(sysTime, SysTime(DateTime(-2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(9); + assertPred!"=="(sysTime, SysTime(DateTime(-1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(-2000, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } + + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 3, 1, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } + + //Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8); + assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8); + assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8); + assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8); + assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(Date(1, 3, 1))); + } + + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(Date(-1, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(DateTime(1, 3, 1, 5, 5, 5), FracSec.from!"msecs"(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(DateTime(-1, 3, 1, 5, 5, 5), FracSec.from!"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))); + + //Verify Examples. + 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 AllowDayOverlow.no + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(2000, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); + } + + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2000, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } + + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } + + //Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1, 2, 28))); + } + + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(-5); + assertPred!"=="(sysTime, SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(5); + assertPred!"=="(sysTime, SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); + } + } + + //Test add!"months"() with AllowDayOverlow.yes + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6); + assertPred!"=="(sysTime, SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27); + assertPred!"=="(sysTime, SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12); + assertPred!"=="(sysTime, SysTime(Date(2001, 3, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 1))); + sysTime.add!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(1999, 3, 3))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(1998, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(2000, 3, 2))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 2))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(2001, 3, 3))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(2000, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(2000, 3, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 1, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(2001, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(2000, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3); + assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6); + assertPred!"=="(sysTime, SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27); + assertPred!"=="(sysTime, SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12); + assertPred!"=="(sysTime, SysTime(Date(-2001, 3, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(-1997, 10, 1))); + sysTime.add!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(-1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(-1995, 3, 3))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(-1996, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(-2000, 3, 2))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(-2001, 1, 2))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(-1999, 3, 3))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(-2000, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(-2000, 3, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(-2001, 1, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(-2000, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48); + assertPred!"=="(sysTime, SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48); + assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49); + assertPred!"=="(sysTime, SysTime(Date(0, 3, 2))); + sysTime.add!"months"(49); + assertPred!"=="(sysTime, SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85); + assertPred!"=="(sysTime, SysTime(Date(-3, 3, 3))); + sysTime.add!"months"(85); + assertPred!"=="(sysTime, SysTime(Date(4, 4, 3))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.add!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.add!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(-85); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 3, 3, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(85); + assertPred!"=="(sysTime, SysTime(DateTime(4, 4, 3, 12, 11, 10), FracSec.from!"msecs"(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(85); + assertPred!"=="(sysTime, SysTime(DateTime(4, 5, 1, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(-85); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 4, 1, 12, 11, 10), FracSec.from!"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))); + + //Verify Examples. + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30 ,33)); + st1.add!"months"(1); + assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 30 ,33))); + + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30 ,33)); + st2.add!"months"(-1); + assert(st2 == SysTime(DateTime(2009, 12, 1, 12, 30 ,33))); + + auto st3 = SysTime(DateTime(1999, 1, 29, 12, 30 ,33)); + st3.add!"months"(1); + assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 30 ,33))); + + auto st4 = SysTime(DateTime(1999, 1, 29, 12, 30 ,33)); + st4.add!"months"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 30 ,33))); + } + + //Test add!"months"() with AllowDayOverlow.no + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2001, 2, 28))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1998, 12, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2001, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(2000, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1998, 12, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(2001, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2001, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1995, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2002, 12, 29))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2001, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2000, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2002, 12, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2001, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(0, 2, 29))); + sysTime.add!"months"(49, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-3, 2, 28))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 3, 28))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 2, 28, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(4, 3, 28, 12, 11, 10), FracSec.from!"msecs"(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(4, 4, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 3, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + } + } + + + /++ + Adds the given number of years to this SysTime. A negative number will + subtract. + + For years, because they are the largest unit in SysTime, there is no + difference between adding or rolling. + + Params: + years = The number of years to add to this SysTime. + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + /+ref SysTime+/ void roll(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) nothrow + if(units == "years") + { + add!"years"(years, allowOverflow); + } + + 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)); + static assert(__traits(compiles, st.roll!"years"(4))); + static assert(!__traits(compiles, cst.roll!"years"(4))); + //static assert(!__traits(compiles, ist.roll!"years"(4))); + } + + + /++ + Adds the given number of months to this SysTime. A negative number will + subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the SysTime 12 months, you get the exact + same SysTime. However, the days can still be affected due to the differing + number of days in each month. + + Params: + months = The number of months to add to this SysTime. + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + + Examples: +-------------------- +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))); +-------------------- + +/ + /+ref SysTime+/ void roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) nothrow + 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"(months, allowOverflow); + days = date.dayOfGregorianCal - 1; + + if(days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + } + + //Test roll!"months"() with AllowDayOverlow.yes + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(1998, 10, 1))); + sysTime.roll!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(1997, 3, 3))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(1997, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(1998, 3, 3))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(1998, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(1999, 3, 3))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.roll!"months"(3); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.roll!"months"(-4); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(1998, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(1998, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3); + assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6); + assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27); + assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12); + assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(-1998, 10, 1))); + sysTime.roll!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(-1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13); + assertPred!"=="(sysTime, SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13); + assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(-1997, 3, 3))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(-1997, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(-2002, 3, 3))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(-2002, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(Date(-2001, 3, 3))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(Date(-2001, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007)); + sysTime.roll!"months"(3); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007))); + sysTime.roll!"months"(-4); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(-2002, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(-2002, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assertPred!"=="(sysTime, SysTime(DateTime(-2001, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assertPred!"=="(sysTime, SysTime(DateTime(-2001, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48); + assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48); + assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49); + assertPred!"=="(sysTime, SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(49); + assertPred!"=="(sysTime, SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85); + assertPred!"=="(sysTime, SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(85); + assertPred!"=="(sysTime, SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(Date(-1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48); + assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48); + assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49); + assertPred!"=="(sysTime, SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(49); + assertPred!"=="(sysTime, SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85); + assertPred!"=="(sysTime, SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(85); + assertPred!"=="(sysTime, SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.roll!"months"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.roll!"months"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(-85); + assertPred!"=="(sysTime, SysTime(DateTime(4, 3, 2, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(85); + assertPred!"=="(sysTime, SysTime(DateTime(4, 4, 2, 12, 11, 10), FracSec.from!"msecs"(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(85); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 5, 1, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(-85); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 4, 1, 12, 11, 10), FracSec.from!"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.roll!"months"(4))); + //static assert(!__traits(compiles, ist.roll!"months"(4))); + + //Verify Examples. + 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))); + } + + //Test roll!"months"() with AllowDayOverlow.no + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1998, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1998, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1999, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1998, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1998, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2002, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2002, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2001, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-2001, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2002, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2002, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2001, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-2001, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } + + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(Date(-4, 3, 29))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(4, 2, 29, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(4, 3, 29, 12, 11, 10), FracSec.from!"msecs"(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 4, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assertPred!"=="(sysTime, SysTime(DateTime(-3, 3, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + } + } + + + /++ + Adds the given number of days to this SysTime. A negative number will + subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the SysTime one year's worth of days, then + you get the exact same SysTime. + + Note that TimeOfDay has no $(D add!"days"()) function because you can add + days to a SysTime by adding a duration to it. + + Params: + days = The number of days to add to this SysTime. + + Examples: +-------------------- +auto st = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); +st.roll!"days"(1); +assert(st == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); +st.roll!"days"(365); +assert(st == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); +st.roll!"days"(-32); +assert(st == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); +-------------------- + +/ + /+ref SysTime+/ void roll(string units)(long days) nothrow + if(units == "days") + { + auto hnsecs = adjTime; + auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --gdays; + } + + auto date = Date(cast(int)gdays); + date.roll!"days"(days); + gdays = date.dayOfGregorianCal - 1; + + if(gdays < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++gdays; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); + + adjTime = newDaysHNSecs + hnsecs; + } + + unittest + { + //Test A.D. + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 28)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 6, 30)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(1999, 1, 1)); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 31))); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(1999, 1, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(9); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 15))); + sysTime.roll!"days"(-11); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 4))); + sysTime.roll!"days"(30); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 3))); + sysTime.roll!"days"(-3); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(365); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 30))); + sysTime.roll!"days"(-365); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); + sysTime.roll!"days"(366); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 31))); + sysTime.roll!"days"(730); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 17))); + sysTime.roll!"days"(-1096); + assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 6)); + sysTime.roll!"days"(365); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 7))); + sysTime.roll!"days"(-365); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 6))); + sysTime.roll!"days"(366); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 8))); + sysTime.roll!"days"(730); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 10))); + sysTime.roll!"days"(-1096); + assertPred!"=="(sysTime, SysTime(Date(1999, 2, 6))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 1, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(9); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 15, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-11); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 4, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(30); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 3, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-3); + assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 31, 7, 9, 2), FracSec.from!"usecs"(234578))); + } + + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 28)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 6, 30)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 1))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(-1999, 1, 1)); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 31))); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(9); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 15))); + sysTime.roll!"days"(-11); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 4))); + sysTime.roll!"days"(30); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 3))); + sysTime.roll!"days"(-3); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(365); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 30))); + sysTime.roll!"days"(-365); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); + sysTime.roll!"days"(366); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 31))); + sysTime.roll!"days"(730); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 17))); + sysTime.roll!"days"(-1096); + assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 1, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(9); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 7, 15, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-11); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 7, 4, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(30); + assertPred!"=="(sysTime, SysTime(DateTime(-1999, 7, 3, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-3); + } + + //Test Both + { + auto sysTime = SysTime(Date(1, 7, 6)); + sysTime.roll!"days"(-365); + assertPred!"=="(sysTime, SysTime(Date(1, 7, 13))); + sysTime.roll!"days"(365); + assertPred!"=="(sysTime, SysTime(Date(1, 7, 6))); + sysTime.roll!"days"(-731); + assertPred!"=="(sysTime, SysTime(Date(1, 7, 19))); + sysTime.roll!"days"(730); + assertPred!"=="(sysTime, SysTime(Date(1, 7, 5))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"days"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"days"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); + sysTime.roll!"days"(-365); + assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 13, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(365); + assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(-731); + assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 19, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(730); + assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 5, 13, 13, 9), FracSec.from!"msecs"(22))); + } + + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); + sysTime.roll!"days"(-365); + assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 13, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(365); + assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(-731); + assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 19, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(730); + assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 5, 13, 13, 9), FracSec.from!"msecs"(22))); + } + + 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.roll!"days"(4))); + //static assert(!__traits(compiles, ist.roll!"days"(4))); + + //Verify Examples. + auto st = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); + st.roll!"days"(1); + assert(st == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); + st.roll!"days"(365); + assert(st == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); + st.roll!"days"(-32); + assert(st == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); + } + + + + /++ + Add hours to the time of day. Negative values will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. So, if you roll the SysTime 24 hours, you get + the exact same SysTime. + + Note that SysTime has no $(D add!"hours"()), $(D add!"minutes"()), or + $(D add!"seconds"()) function because you can add those units to + a SysTime by adding a duration to it. + + Params: + hours = The number of hours to add to this SysTime. + + Examples: +-------------------- +auto st1 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); +st1.roll!"hours"(1); +assert(st1 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); + +auto st2 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); +st2.roll!"hours"(-1); +assert(st2 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); + +auto st3 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); +st3.roll!"minutes"(1); +assert(st3 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); + +auto st4 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); +st4.roll!"minutes"(-1); +assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); + +auto st5 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); +st5.roll!"seconds"(1); +assert(st5 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); + +auto st6 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); +st6.roll!"seconds"(-1); +assert(st6 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); +-------------------- + +/ + /+ref SysTime+/ void roll(string units)(long value) nothrow + if(units == "hours" || + units == "minutes" || + units == "seconds") + { + try + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); + dateTime.roll!units(value); + --days; + + hnsecs += convert!("hours", "hnsecs")(dateTime.hour); + hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); + hnsecs += convert!("seconds", "hnsecs")(dateTime.second); + + if(days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + } + catch(Exception e) + assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); + } + + //Test roll!"hours"(). + unittest + { + static void TestST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + //Test Both + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(45)), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(45)), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(45))); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"hours"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"hours"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"hours"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"hours"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"hours"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"hours"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"hours"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"hours"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + 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.roll!"hours"(4))); + //static assert(!__traits(compiles, ist.roll!"hours"(4))); + + //Verify Examples. + auto st1 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); + st1.roll!"hours"(1); + assert(st1 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); + + auto st2 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); + st2.roll!"hours"(-1); + assert(st2 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); + + auto st3 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st3.roll!"minutes"(1); + assert(st3 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); + + auto st4 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st4.roll!"minutes"(-1); + assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); + + auto st5 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st5.roll!"seconds"(1); + assert(st5 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); + + auto st6 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st6.roll!"seconds"(-1); + assert(st6 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); + } + + //Test roll!"minutes"(). + unittest + { + static void TestST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), FracSec.from!"usecs"(7203))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), FracSec.from!"usecs"(7203))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); + + TestST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); + TestST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203)), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203)), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203)), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1, 1, 1, 13, 52, 33), FracSec.from!"usecs"(7203)), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203))); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"minutes"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 59, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"minutes"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"minutes"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"minutes"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"minutes"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"minutes"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"minutes"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"minutes"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + 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.roll!"minutes"(4))); + //static assert(!__traits(compiles, ist.roll!"minutes"(4))); + } + + //Test roll!"seconds"(). + unittest + { + static void TestST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), FracSec.from!"msecs"(274))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), FracSec.from!"msecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274)), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274)), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274)), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 50), FracSec.from!"msecs"(274)), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274))); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"seconds"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(0))); + sysTime.roll!"seconds"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"seconds"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"seconds"(1); + assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)); + sysTime.roll!"seconds"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"seconds"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"seconds"(1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"seconds"(-1); + assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + 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.roll!"seconds"(4))); + //static assert(!__traits(compiles, ist.roll!"seconds"(4))); + } + + + /++ + Add to the fractional seconds of the time of day. Negative values will + subtract. + + The difference between rolling and adding is that rolling does not affect + larger units. So, if you roll the SysTime 1 second, you get the exact same + SysTime. + + Note that SysTime has no $(D add!"msecs"()), $(D add!"usecs"()), or + $(D add!"hnsecs"()) function because you can add those units to + a SysTime by adding a duration to it. + + Params: + units = The units to add to this SysTime. + value = The number of units to add to this SysTime. + +/ + /+ref SysTime+/ void roll(string units)(long value) nothrow + if(units == "msecs" || + units == "usecs" || + units == "hnsecs") + { + auto hnsecs = adjTime; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable negative = hnsecs < 0; + + if(negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + hnsecs += convert!(units, "hnsecs")(value); + hnsecs %= convert!("seconds", "hnsecs")(1); + + if(hnsecs < 0) + hnsecs += convert!("seconds", "hnsecs")(1); + + hnsecs += convert!("seconds", "hnsecs")(seconds); + + if(negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + } + + + //Test roll!"msecs"(). + unittest + { + static void TestST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"msecs"(milliseconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(1))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(1))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(445))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_989_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(5_549_999))); + + 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.addMSecs(4))); + //static assert(!__traits(compiles, ist.addMSecs(4))); + } + + //Test roll!"usecs"(). + unittest + { + static void TestST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"usecs"(microseconds); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(666_667))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_989))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(25_549))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(3_333_329))); + + 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.roll!"usecs"(4))); + //static assert(!__traits(compiles, ist.roll!"usecs"(4))); + } + + //Test roll!"hnsecs"(). + unittest + { + static void TestST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"hnsecs"(hnsecs); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(8_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7_666_667))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_111_112))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(2554))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(2_333_332))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(888_887))); + + 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.roll!"hnsecs"(4))); + //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); + + //Verify Examples. + auto st1 = SysTime(DateTime(2010, 7, 4, 7, 32, 12)); + st1.roll!"hnsecs"(1); + assert(st1 == SysTime(DateTime(2010, 7, 4, 7, 32, 12), FracSec.from!"hnsecs"(1))); + + auto st2 = SysTime(DateTime(2010, 7, 4, 7, 32, 12)); + st2.roll!"hnsecs"(-1); + assert(st2 == SysTime(DateTime(2010, 7, 4, 7, 32, 12), FracSec.from!"hnsecs"(9_999_999))); + + auto st3 = SysTime(DateTime(2009, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + st3.roll!"hnsecs"(1); + assert(st3 == SysTime(DateTime(2009, 12, 31, 23, 59, 59))); + + auto st4 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st4.roll!"hnsecs"(-1); + assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + + auto st5 = SysTime(DateTime(2009, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + st5.roll!"hnsecs"(1); + assert(st5 == SysTime(DateTime(2009, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + } + + + /++ + Gives the result of adding or subtracting a duration from this SysTime. + + The legal types of arithmetic for SysTime using this operator are + + $(TABLE + $(TR $(TD SysTime) $(TD +) $(TD duration) $(TD -->) $(TD SysTime)) + $(TR $(TD SysTime) $(TD -) $(TD duration) $(TD -->) $(TD SysTime)) + ) + + Params: + duration = The duration to add to or subtract from this SysTime. + +/ + SysTime opBinary(string op, D)(in D duration) const pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + SysTime retval = SysTime(this._stdTime, this._timezone.get); + + static if(is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if(is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + //Ideally, this would just be + //retval._stdTime += unaryFun!(op ~ "a")(hnsecs); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedHNSecs = hnsecs; + else static if(op == "-") + immutable signedHNSecs = -hnsecs; + else + static assert(0); + + retval._stdTime += signedHNSecs; + + return retval; + } + + unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678)); + + assertPred!"=="(st + dur!"weeks"(7), SysTime(DateTime(1999, 8, 24, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"weeks"(-7), SysTime(DateTime(1999, 5, 18, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"days"(7), SysTime(DateTime(1999, 7, 13, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"days"(-7), SysTime(DateTime(1999, 6, 29, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 37, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 23, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 40), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 26), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st + dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_415_678))); + assertPred!"=="(st + dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_275_678))); + assertPred!"=="(st + dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assertPred!"=="(st + dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + assertPred!"=="(st + dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_685))); + assertPred!"=="(st + dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_671))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(st + TickDuration.from!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assertPred!"=="(st + TickDuration.from!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + } + + assertPred!"=="(st - dur!"weeks"(-7), SysTime(DateTime(1999, 8, 24, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"weeks"(7), SysTime(DateTime(1999, 5, 18, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"days"(-7), SysTime(DateTime(1999, 7, 13, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"days"(7), SysTime(DateTime(1999, 6, 29, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 37, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 23, 33), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 40), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 26), FracSec.from!"hnsecs"(2_345_678))); + assertPred!"=="(st - dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_415_678))); + assertPred!"=="(st - dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_275_678))); + assertPred!"=="(st - dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assertPred!"=="(st - dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + assertPred!"=="(st - dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_685))); + assertPred!"=="(st - dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_671))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assertPred!"=="(st - TickDuration.from!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assertPred!"=="(st - TickDuration.from!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + } + + static void TestST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + assertPred!"=="(orig + dur!"hnsecs"(hnsecs), expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7_666_667))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), FracSec.from!"hnsecs"(9_111_112))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2554))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2_333_332))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), FracSec.from!"hnsecs"(888_887))); + + auto duration = dur!"seconds"(12); + 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 + duration)); + //static assert(__traits(compiles, ist + duration)); + static assert(__traits(compiles, cst - duration)); + //static assert(__traits(compiles, ist - duration)); + } + + + /++ + Gives the result of adding or subtracting a duration from this SysTime, + as well as assigning the result to this SysTime. + + The legal types of arithmetic for SysTime using this operator are + + $(TABLE + $(TR $(TD SysTime) $(TD +) $(TD duration) $(TD -->) $(TD SysTime)) + $(TR $(TD SysTime) $(TD -) $(TD duration) $(TD -->) $(TD SysTime)) + ) + + Params: + duration = The duration to add to or subtract from this SysTime. + +/ + /+ref+/ SysTime opOpAssign(string op, D)(in D duration) pure nothrow + if((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + static if(is(Unqual!D == Duration)) + auto hnsecs = duration.total!"hnsecs"; + else static if(is(Unqual!D == TickDuration)) + auto hnsecs = duration.hnsecs; + + //Ideally, this would just be + //_stdTime += unaryFun!(op ~ "a")(hnsecs); + //But there isn't currently a pure version of unaryFun!(). + + static if(op == "+") + immutable signedHNSecs = hnsecs; + else static if(op == "-") + immutable signedHNSecs = -hnsecs; + else + static assert(0); + + _stdTime += signedHNSecs; + + return this; + } + + unittest + { + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(7), SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(-7), SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(7), SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(-7), SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(7))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(993))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"usecs"(999_993))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(7))); + assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_993))); + + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(-7), SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(7), SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(-7), SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(7), SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(7))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(993))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"usecs"(999_993))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(7))); + assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_993))); + + static void TestST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + orig += dur!"hnsecs"(hnsecs); + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7_666_667))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), FracSec.from!"hnsecs"(9_111_112))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2554))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2_333_332))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), FracSec.from!"hnsecs"(888_887))); + + auto duration = dur!"seconds"(12); + 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 += duration)); + //static assert(!__traits(compiles, ist += duration)); + static assert(!__traits(compiles, cst -= duration)); + //static assert(!__traits(compiles, ist -= duration)); + } + + + /++ + Gives the difference between two SysTimes. + + The legal types of arithmetic for SysTime using this operator are + + $(TABLE + $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in SysTime rhs) const pure nothrow + if(op == "-") + { + return dur!"hnsecs"(_stdTime - rhs._stdTime); + } + + unittest + { + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)), + dur!"seconds"(31_536_000)); + assertPred!"=="(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"seconds"(-31_536_000)); + + assertPred!"=="(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"seconds"(26_78_400)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)), + dur!"seconds"(-26_78_400)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)), + dur!"seconds"(86_400)); + assertPred!"=="(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"seconds"(-86_400)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)), + dur!"seconds"(3600)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"seconds"(-3600)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"seconds"(60)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)), + dur!"seconds"(-60)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"seconds"(1)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)), + dur!"seconds"(-1)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(532)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"msecs"(532)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(532)), + dur!"msecs"(-532)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(333_347)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"usecs"(333_347)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(333_347)), + dur!"usecs"(-333_347)); + + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_234_567)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), + dur!"hnsecs"(1_234_567)); + assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_234_567)), + dur!"hnsecs"(-1_234_567)); + + assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)), dur!"seconds"(45033)); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)), dur!"seconds"(-45033)); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)), dur!"seconds"(-41367)); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)), dur!"seconds"(41367)); + + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), dur!"hnsecs"(1)); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)), dur!"hnsecs"(-1)); + + 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)); + static assert(__traits(compiles, st - st)); + static assert(__traits(compiles, cst - st)); + //static assert(__traits(compiles, ist - st)); + + static assert(__traits(compiles, st - cst)); + static assert(__traits(compiles, cst - cst)); + //static assert(__traits(compiles, ist - cst)); + + //static assert(__traits(compiles, st - ist)); + //static assert(__traits(compiles, cst - ist)); + //static assert(__traits(compiles, ist - ist)); + } + + + /++ + Returns the difference between the two SysTimes in months. + + You can get the difference in years by subtracting the year property + of two SysTimes, and you can get the difference in days or weeks by + subtracting the SysTimes themselves and using the Duration that results, + but because you cannot convert between months and smaller units without + a specific date (which the Duration from the subtraction of two SysTimes + won't have), you cannot get the difference in months without doing some + math using both the year and month properties, so this is a convenience + function for getting the difference in months. + + Note that the number of days in the months or how far into the month either + date is is irrelevant. It is the difference in the month property combined + with the difference in years * 12. So, for instance, December 31st and + January 1st are one month apart just as December 1st and January 31st are + one month apart. + + Params: + rhs = The SysTime to subtract from this one. + + Examples: +-------------------- +assert(SysTime(Date(1999, 2, 1)).diffMonths(SysTime(Date(1999, 1, 31))) == 1); +assert(SysTime(Date(1999, 1, 31)).diffMonths(SysTime(Date(1999, 2, 1))) == -1); +assert(SysTime(Date(1999, 3, 1)).diffMonths(SysTime(Date(1999, 1, 1))) == 2); +assert(SysTime(Date(1999, 1, 1)).diffMonths(SysTime(Date(1999, 3, 31))) == -2); +-------------------- + +/ + int diffMonths(in SysTime rhs) const nothrow + { + return (cast(Date)this).diffMonths(cast(Date)rhs); + } + + 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)); + static assert(__traits(compiles, st.diffMonths(st))); + static assert(__traits(compiles, cst.diffMonths(st))); + //static assert(__traits(compiles, ist.diffMonths(st))); + + static assert(__traits(compiles, st.diffMonths(cst))); + static assert(__traits(compiles, cst.diffMonths(cst))); + //static assert(__traits(compiles, ist.diffMonths(cst))); + + //static assert(__traits(compiles, st.diffMonths(ist))); + //static assert(__traits(compiles, cst.diffMonths(ist))); + //static assert(__traits(compiles, ist.diffMonths(ist))); + + //Verify Examples. + assert(SysTime(Date(1999, 2, 1)).diffMonths(SysTime(Date(1999, 1, 31))) == 1); + assert(SysTime(Date(1999, 1, 31)).diffMonths(SysTime(Date(1999, 2, 1))) == -1); + assert(SysTime(Date(1999, 3, 1)).diffMonths(SysTime(Date(1999, 1, 1))) == 2); + assert(SysTime(Date(1999, 1, 1)).diffMonths(SysTime(Date(1999, 3, 31))) == -2); + } + + + /++ + Whether this SysTime is in a leap year. + +/ + @property bool isLeapYear() const nothrow + { + return (cast(Date)this).isLeapYear; + } + + 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)); + static assert(__traits(compiles, st.isLeapYear)); + static assert(__traits(compiles, cst.isLeapYear)); + //static assert(__traits(compiles, ist.isLeapYear)); + } + + + /++ + Day of the week this SysTime is on. + +/ + @property DayOfWeek dayOfWeek() const nothrow + { + return getDayOfWeek(dayOfGregorianCal); + } + + 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)); + static assert(__traits(compiles, st.dayOfWeek)); + static assert(__traits(compiles, cst.dayOfWeek)); + //static assert(__traits(compiles, ist.dayOfWeek)); + } + + + /++ + Day of the year this SysTime is on. + + Examples: +-------------------- +assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); +assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); +assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); +-------------------- + +/ + @property ushort dayOfYear() const nothrow + { + return (cast(Date)this).dayOfYear; + } + + 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)); + static assert(__traits(compiles, st.dayOfYear)); + static assert(__traits(compiles, cst.dayOfYear)); + //static assert(__traits(compiles, ist.dayOfYear)); + + //Verify Examples. + assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); + assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); + assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this SysTime is on. + +/ + @property void dayOfYear(int day) + { + immutable hnsecs = adjTime; + immutable days = convert!("hnsecs", "days")(hnsecs); + immutable theRest = hnsecs - convert!("days", "hnsecs")(days); + + auto date = Date(cast(int)days); + date.dayOfYear = day; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + + adjTime = newDaysHNSecs + theRest; + } + + 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)); + static assert(__traits(compiles, st.dayOfYear = 12)); + static assert(!__traits(compiles, cst.dayOfYear = 12)); + //static assert(!__traits(compiles, ist.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this SysTime is on. + + Examples: +-------------------- +assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); +assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); +assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); + +assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); +assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); +assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); + +assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); +assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); +-------------------- + +/ + @property int dayOfGregorianCal() const nothrow + { + immutable adjustedTime = adjTime; + + //We have to add one because 0 would be midnight, January 1st, 1 A.D., which would be + //the 1st day of the Gregorian Calendar, not the 0th. So, simply casting to days + //is one day off. + if(adjustedTime > 0) + return cast(int)getUnitsFromHNSecs!"days"(adjustedTime) + 1; + + auto hnsecs = adjustedTime; + immutable days = cast(int)splitUnitsFromHNSecs!"days"(hnsecs); + + return hnsecs == 0 ? days + 1 : days; + } + + unittest + { + //Test A.D. + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, 1); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)).dayOfGregorianCal, 1); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal, 1); + + assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 1); + assertPred!"=="(SysTime(DateTime(1, 1, 2, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 2); + assertPred!"=="(SysTime(DateTime(1, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 32); + assertPred!"=="(SysTime(DateTime(2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 366); + assertPred!"=="(SysTime(DateTime(3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 731); + assertPred!"=="(SysTime(DateTime(4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 1096); + assertPred!"=="(SysTime(DateTime(5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 1462); + assertPred!"=="(SysTime(DateTime(50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 17_898); + assertPred!"=="(SysTime(DateTime(97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 35_065); + assertPred!"=="(SysTime(DateTime(100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 36_160); + assertPred!"=="(SysTime(DateTime(101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 36_525); + assertPred!"=="(SysTime(DateTime(105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 37_986); + assertPred!"=="(SysTime(DateTime(200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 72_684); + assertPred!"=="(SysTime(DateTime(201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 73_049); + assertPred!"=="(SysTime(DateTime(300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 109_208); + assertPred!"=="(SysTime(DateTime(301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 109_573); + assertPred!"=="(SysTime(DateTime(400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 145_732); + assertPred!"=="(SysTime(DateTime(401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 146_098); + assertPred!"=="(SysTime(DateTime(500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 182_257); + assertPred!"=="(SysTime(DateTime(501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 182_622); + assertPred!"=="(SysTime(DateTime(1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 364_878); + assertPred!"=="(SysTime(DateTime(1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 365_243); + assertPred!"=="(SysTime(DateTime(1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 584_023); + assertPred!"=="(SysTime(DateTime(1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 584_389); + assertPred!"=="(SysTime(DateTime(1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 693_596); + assertPred!"=="(SysTime(DateTime(1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 693_961); + assertPred!"=="(SysTime(DateTime(1945, 11, 12, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 710_347); + assertPred!"=="(SysTime(DateTime(1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 729_755); + assertPred!"=="(SysTime(DateTime(2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 730_120); + assertPred!"=="(SysTime(DateTime(2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 730_486); + + assertPred!"=="(SysTime(DateTime(2010, 1, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_773); + assertPred!"=="(SysTime(DateTime(2010, 1, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_803); + assertPred!"=="(SysTime(DateTime(2010, 2, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_804); + assertPred!"=="(SysTime(DateTime(2010, 2, 28, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_831); + assertPred!"=="(SysTime(DateTime(2010, 3, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_832); + assertPred!"=="(SysTime(DateTime(2010, 3, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_862); + assertPred!"=="(SysTime(DateTime(2010, 4, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_863); + assertPred!"=="(SysTime(DateTime(2010, 4, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_892); + assertPred!"=="(SysTime(DateTime(2010, 5, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_893); + assertPred!"=="(SysTime(DateTime(2010, 5, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_923); + assertPred!"=="(SysTime(DateTime(2010, 6, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_924); + assertPred!"=="(SysTime(DateTime(2010, 6, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_953); + assertPred!"=="(SysTime(DateTime(2010, 7, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_954); + assertPred!"=="(SysTime(DateTime(2010, 7, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_984); + assertPred!"=="(SysTime(DateTime(2010, 8, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_985); + assertPred!"=="(SysTime(DateTime(2010, 8, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_015); + assertPred!"=="(SysTime(DateTime(2010, 9, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_016); + assertPred!"=="(SysTime(DateTime(2010, 9, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_045); + assertPred!"=="(SysTime(DateTime(2010, 10, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_046); + assertPred!"=="(SysTime(DateTime(2010, 10, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_076); + assertPred!"=="(SysTime(DateTime(2010, 11, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_077); + assertPred!"=="(SysTime(DateTime(2010, 11, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_106); + assertPred!"=="(SysTime(DateTime(2010, 12, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_107); + assertPred!"=="(SysTime(DateTime(2010, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_137); + + assertPred!"=="(SysTime(DateTime(2012, 2, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_534); + assertPred!"=="(SysTime(DateTime(2012, 2, 28, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_561); + assertPred!"=="(SysTime(DateTime(2012, 2, 29, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_562); + assertPred!"=="(SysTime(DateTime(2012, 3, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_563); + + //Test B.C. + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal, 0); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998)).dayOfGregorianCal, 0); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, 0); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(1)).dayOfGregorianCal, 0); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, 0); + + assertPred!"=="(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal, -366); + assertPred!"=="(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998)).dayOfGregorianCal, -366); + assertPred!"=="(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, -366); + assertPred!"=="(SysTime(DateTime(-1, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, -366); + + assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 0); + assertPred!"=="(SysTime(DateTime(0, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1); + assertPred!"=="(SysTime(DateTime(0, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -30); + assertPred!"=="(SysTime(DateTime(0, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -31); + + assertPred!"=="(SysTime(DateTime(-1, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -366); + assertPred!"=="(SysTime(DateTime(-1, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -367); + assertPred!"=="(SysTime(DateTime(-1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730); + assertPred!"=="(SysTime(DateTime(-2, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -731); + assertPred!"=="(SysTime(DateTime(-2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1095); + assertPred!"=="(SysTime(DateTime(-3, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1096); + assertPred!"=="(SysTime(DateTime(-3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1460); + assertPred!"=="(SysTime(DateTime(-4, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1461); + assertPred!"=="(SysTime(DateTime(-4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1826); + assertPred!"=="(SysTime(DateTime(-5, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1827); + assertPred!"=="(SysTime(DateTime(-5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -2191); + assertPred!"=="(SysTime(DateTime(-9, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -3652); + + assertPred!"=="(SysTime(DateTime(-49, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -18_262); + assertPred!"=="(SysTime(DateTime(-50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -18_627); + assertPred!"=="(SysTime(DateTime(-97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -35_794); + assertPred!"=="(SysTime(DateTime(-99, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -36_160); + assertPred!"=="(SysTime(DateTime(-99, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -36_524); + assertPred!"=="(SysTime(DateTime(-100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -36_889); + assertPred!"=="(SysTime(DateTime(-101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -37_254); + assertPred!"=="(SysTime(DateTime(-105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -38_715); + assertPred!"=="(SysTime(DateTime(-200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -73_413); + assertPred!"=="(SysTime(DateTime(-201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -73_778); + assertPred!"=="(SysTime(DateTime(-300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -109_937); + assertPred!"=="(SysTime(DateTime(-301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -110_302); + assertPred!"=="(SysTime(DateTime(-400, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -146_097); + assertPred!"=="(SysTime(DateTime(-400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -146_462); + assertPred!"=="(SysTime(DateTime(-401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -146_827); + assertPred!"=="(SysTime(DateTime(-499, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -182_621); + assertPred!"=="(SysTime(DateTime(-500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -182_986); + assertPred!"=="(SysTime(DateTime(-501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -183_351); + assertPred!"=="(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -365_607); + assertPred!"=="(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -365_972); + assertPred!"=="(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -584_387); + assertPred!"=="(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -584_388); + assertPred!"=="(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -584_753); + assertPred!"=="(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -585_118); + assertPred!"=="(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -694_325); + assertPred!"=="(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -694_690); + assertPred!"=="(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730_484); + assertPred!"=="(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730_485); + assertPred!"=="(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730_850); + assertPred!"=="(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -731_215); + + assertPred!"=="(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_502); + assertPred!"=="(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_472); + assertPred!"=="(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_471); + assertPred!"=="(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_444); + assertPred!"=="(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_443); + assertPred!"=="(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_413); + assertPred!"=="(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_412); + assertPred!"=="(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_383); + assertPred!"=="(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_382); + assertPred!"=="(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_352); + assertPred!"=="(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_351); + assertPred!"=="(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_322); + assertPred!"=="(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_321); + assertPred!"=="(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_291); + assertPred!"=="(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_290); + assertPred!"=="(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_260); + assertPred!"=="(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_259); + assertPred!"=="(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_230); + assertPred!"=="(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_229); + assertPred!"=="(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_199); + assertPred!"=="(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_198); + assertPred!"=="(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_169); + assertPred!"=="(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_168); + assertPred!"=="(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_138); + + assertPred!"=="(SysTime(DateTime(-2012, 2, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_202); + assertPred!"=="(SysTime(DateTime(-2012, 2, 28, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_175); + assertPred!"=="(SysTime(DateTime(-2012, 2, 29, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_174); + assertPred!"=="(SysTime(DateTime(-2012, 3, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_173); + + assertPred!"=="(SysTime(DateTime(-3760, 9, 7, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -1_373_427); //Start of Hebrew Calendar + + 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.dayOfGregorianCal)); + //static assert(__traits(compiles, ist.dayOfGregorianCal)); + + //Verify Examples. + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); + assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); + + assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); + assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); + } + + + //Test that the logic for the day of the Gregorian Calendar is consistent + //between Date and SysTime. + unittest + { + //Test A.D. + assertPred!"=="(Date(1, 1, 1).dayOfGregorianCal, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(1, 1, 2).dayOfGregorianCal, SysTime(DateTime(1, 1, 2, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(1, 2, 1).dayOfGregorianCal, SysTime(DateTime(1, 2, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(2, 1, 1).dayOfGregorianCal, SysTime(DateTime(2, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(3, 1, 1).dayOfGregorianCal, SysTime(DateTime(3, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(4, 1, 1).dayOfGregorianCal, SysTime(DateTime(4, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(5, 1, 1).dayOfGregorianCal, SysTime(DateTime(5, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(50, 1, 1).dayOfGregorianCal, SysTime(DateTime(50, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(97, 1, 1).dayOfGregorianCal, SysTime(DateTime(97, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(100, 1, 1).dayOfGregorianCal, SysTime(DateTime(100, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(101, 1, 1).dayOfGregorianCal, SysTime(DateTime(101, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(105, 1, 1).dayOfGregorianCal, SysTime(DateTime(105, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(200, 1, 1).dayOfGregorianCal, SysTime(DateTime(200, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(201, 1, 1).dayOfGregorianCal, SysTime(DateTime(201, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(300, 1, 1).dayOfGregorianCal, SysTime(DateTime(300, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(301, 1, 1).dayOfGregorianCal, SysTime(DateTime(301, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(400, 1, 1).dayOfGregorianCal, SysTime(DateTime(400, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(401, 1, 1).dayOfGregorianCal, SysTime(DateTime(401, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(500, 1, 1).dayOfGregorianCal, SysTime(DateTime(500, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(501, 1, 1).dayOfGregorianCal, SysTime(DateTime(501, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(1000, 1, 1).dayOfGregorianCal, SysTime(DateTime(1000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(1001, 1, 1).dayOfGregorianCal, SysTime(DateTime(1001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(1600, 1, 1).dayOfGregorianCal, SysTime(DateTime(1600, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(1601, 1, 1).dayOfGregorianCal, SysTime(DateTime(1601, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(1900, 1, 1).dayOfGregorianCal, SysTime(DateTime(1900, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(1901, 1, 1).dayOfGregorianCal, SysTime(DateTime(1901, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(1945, 11, 12).dayOfGregorianCal, SysTime(DateTime(1945, 11, 12, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(1999, 1, 1).dayOfGregorianCal, SysTime(DateTime(1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(1999, 7, 6).dayOfGregorianCal, SysTime(DateTime(1999, 7, 6, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2000, 1, 1).dayOfGregorianCal, SysTime(DateTime(2000, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2001, 1, 1).dayOfGregorianCal, SysTime(DateTime(2001, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assertPred!"=="(Date(2010, 1, 1).dayOfGregorianCal, SysTime(DateTime(2010, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 1, 31).dayOfGregorianCal, SysTime(DateTime(2010, 1, 31, 23, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 2, 1).dayOfGregorianCal, SysTime(DateTime(2010, 2, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 2, 28).dayOfGregorianCal, SysTime(DateTime(2010, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 3, 1).dayOfGregorianCal, SysTime(DateTime(2010, 3, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 3, 31).dayOfGregorianCal, SysTime(DateTime(2010, 3, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 4, 1).dayOfGregorianCal, SysTime(DateTime(2010, 4, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 4, 30).dayOfGregorianCal, SysTime(DateTime(2010, 4, 30, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 5, 1).dayOfGregorianCal, SysTime(DateTime(2010, 5, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 5, 31).dayOfGregorianCal, SysTime(DateTime(2010, 5, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 6, 1).dayOfGregorianCal, SysTime(DateTime(2010, 6, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 6, 30).dayOfGregorianCal, SysTime(DateTime(2010, 6, 30, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 7, 1).dayOfGregorianCal, SysTime(DateTime(2010, 7, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 7, 31).dayOfGregorianCal, SysTime(DateTime(2010, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 8, 1).dayOfGregorianCal, SysTime(DateTime(2010, 8, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 8, 31).dayOfGregorianCal, SysTime(DateTime(2010, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 9, 1).dayOfGregorianCal, SysTime(DateTime(2010, 9, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 9, 30).dayOfGregorianCal, SysTime(DateTime(2010, 9, 30, 12, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 10, 1).dayOfGregorianCal, SysTime(DateTime(2010, 10, 1, 0, 12, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 10, 31).dayOfGregorianCal, SysTime(DateTime(2010, 10, 31, 0, 0, 12), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 11, 1).dayOfGregorianCal, SysTime(DateTime(2010, 11, 1, 23, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 11, 30).dayOfGregorianCal, SysTime(DateTime(2010, 11, 30, 0, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 12, 1).dayOfGregorianCal, SysTime(DateTime(2010, 12, 1, 0, 0, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(2010, 12, 31).dayOfGregorianCal, SysTime(DateTime(2010, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assertPred!"=="(Date(2012, 2, 1).dayOfGregorianCal, SysTime(DateTime(2012, 2, 1, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(2012, 2, 28).dayOfGregorianCal, SysTime(DateTime(2012, 2, 28, 23, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(2012, 2, 29).dayOfGregorianCal, SysTime(DateTime(2012, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + assertPred!"=="(Date(2012, 3, 1).dayOfGregorianCal, SysTime(DateTime(2012, 3, 1, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + + //Test B.C. + assertPred!"=="(Date(0, 12, 31).dayOfGregorianCal, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(0, 12, 30).dayOfGregorianCal, SysTime(DateTime(0, 12, 30, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(0, 12, 1).dayOfGregorianCal, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(0, 11, 30).dayOfGregorianCal, SysTime(DateTime(0, 11, 30, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + + assertPred!"=="(Date(-1, 12, 31).dayOfGregorianCal, SysTime(DateTime(-1, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-1, 12, 30).dayOfGregorianCal, SysTime(DateTime(-1, 12, 30, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-1, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-2, 12, 31).dayOfGregorianCal, SysTime(DateTime(-2, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-3, 12, 31).dayOfGregorianCal, SysTime(DateTime(-3, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-3, 1, 1).dayOfGregorianCal, SysTime(DateTime(-3, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-4, 12, 31).dayOfGregorianCal, SysTime(DateTime(-4, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-4, 1, 1).dayOfGregorianCal, SysTime(DateTime(-4, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-5, 12, 31).dayOfGregorianCal, SysTime(DateTime(-5, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-5, 1, 1).dayOfGregorianCal, SysTime(DateTime(-5, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-9, 1, 1).dayOfGregorianCal, SysTime(DateTime(-9, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + + assertPred!"=="(Date(-49, 1, 1).dayOfGregorianCal, SysTime(DateTime(-49, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-50, 1, 1).dayOfGregorianCal, SysTime(DateTime(-50, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-97, 1, 1).dayOfGregorianCal, SysTime(DateTime(-97, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-99, 12, 31).dayOfGregorianCal, SysTime(DateTime(-99, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-99, 1, 1).dayOfGregorianCal, SysTime(DateTime(-99, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-100, 1, 1).dayOfGregorianCal, SysTime(DateTime(-100, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-101, 1, 1).dayOfGregorianCal, SysTime(DateTime(-101, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-105, 1, 1).dayOfGregorianCal, SysTime(DateTime(-105, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-200, 1, 1).dayOfGregorianCal, SysTime(DateTime(-200, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-201, 1, 1).dayOfGregorianCal, SysTime(DateTime(-201, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-300, 1, 1).dayOfGregorianCal, SysTime(DateTime(-300, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-301, 1, 1).dayOfGregorianCal, SysTime(DateTime(-301, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-400, 12, 31).dayOfGregorianCal, SysTime(DateTime(-400, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-400, 1, 1).dayOfGregorianCal, SysTime(DateTime(-400, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-401, 1, 1).dayOfGregorianCal, SysTime(DateTime(-401, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-499, 1, 1).dayOfGregorianCal, SysTime(DateTime(-499, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-500, 1, 1).dayOfGregorianCal, SysTime(DateTime(-500, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-501, 1, 1).dayOfGregorianCal, SysTime(DateTime(-501, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-1000, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-1001, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-1599, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1599, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-1600, 12, 31).dayOfGregorianCal, SysTime(DateTime(-1600, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-1600, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1600, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-1601, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1601, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-1900, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1900, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-1901, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1901, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-1999, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1999, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-1999, 7, 6).dayOfGregorianCal, SysTime(DateTime(-1999, 7, 6, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2000, 12, 31).dayOfGregorianCal, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2000, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2001, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assertPred!"=="(Date(-2010, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 1, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 1, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 2, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 2, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 2, 28).dayOfGregorianCal, SysTime(DateTime(-2010, 2, 28, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 3, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 3, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 3, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 3, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 4, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 4, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 4, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 4, 30, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 5, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 5, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 5, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 6, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 6, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 6, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 7, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 7, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 7, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 7, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 8, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 8, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 8, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 8, 31, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 9, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 9, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 9, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 9, 30, 12, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 10, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 10, 1, 0, 12, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 10, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 10, 31, 0, 0, 12), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 11, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 11, 1, 23, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 11, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 11, 30, 0, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 12, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 12, 1, 0, 0, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assertPred!"=="(Date(-2010, 12, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assertPred!"=="(Date(-2012, 2, 1).dayOfGregorianCal, SysTime(DateTime(-2012, 2, 1, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assertPred!"=="(Date(-2012, 2, 28).dayOfGregorianCal, SysTime(DateTime(-2012, 2, 28, 23, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assertPred!"=="(Date(-2012, 2, 29).dayOfGregorianCal, SysTime(DateTime(-2012, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + assertPred!"=="(Date(-2012, 3, 1).dayOfGregorianCal, SysTime(DateTime(-2012, 3, 1, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + + assertPred!"=="(Date(-3760, 9, 7).dayOfGregorianCal, SysTime(DateTime(-3760, 9, 7, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + } + + + /++ + The Xth day of the Gregorian Calendar that this SysTime is on. Setting + this property does not affect the time portion of SysTime. + + Params: + days = The day of the Gregorian Calendar to set this Date to. + + Examples: +-------------------- +auto st = SysTime(DateTime(0, 0, 0, 12, 0, 0)); +st.dayOfGregorianCal = 1; +assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); + +st.dayOfGregorianCal = 365; +assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); + +st.dayOfGregorianCal = 366; +assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); + +st.dayOfGregorianCal = 0; +assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); + +st.dayOfGregorianCal = -365; +assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); + +st.dayOfGregorianCal = -366; +assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); + +st.dayOfGregorianCal = 730_120; +assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); + +st.dayOfGregorianCal = 734_137; +assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); +-------------------- + +/ + @property void dayOfGregorianCal(int days) nothrow + { + auto hnsecs = adjTime; + hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); + + if(hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + if(--days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + } + + unittest + { + void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) + { + orig.dayOfGregorianCal = day; + assertPred!"=="(orig, expected, "", __FILE__, line); + } + + //Test A.D. + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + //Test B.C. + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + + //Test Both. + testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + + + auto sysTime = SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)); + + void testST2(int day, in SysTime expected, size_t line = __LINE__) + { + sysTime.dayOfGregorianCal = day; + assertPred!"=="(sysTime, expected, "", __FILE__, line); + } + + //Test A.D. + testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + //Test B.C. + testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + 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.dayOfGregorianCal = 7)); + //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); + + //Verify Examples. + auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); + st.dayOfGregorianCal = 1; + assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 365; + assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = 366; + assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 0; + assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = -365; + assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = -366; + assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = 730_120; + assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 734_137; + assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); + } + + + /++ + The ISO 8601 week of the year that this SysTime is in. + + See_Also: + ISO Week Date + +/ + @property ubyte isoWeek() const nothrow + { + return (cast(Date)this).isoWeek; + } + + 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)); + static assert(__traits(compiles, st.isoWeek)); + static assert(__traits(compiles, cst.isoWeek)); + //static assert(__traits(compiles, ist.isoWeek)); + } + + + /++ + SysTime for the last day in the month that this Date is in. + The time portion of endOfMonth is always 23:59:59.9999999. + + Examples: +-------------------- +assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); +assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), FracSec.from!"msecs"(24)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); +assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), FracSec.from!"usecs"(5203)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); +assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), FracSec.from!"hnsecs"(12345)).endOfMonth == SysTime(DateTime(2000, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); +-------------------- + +/ + @property SysTime endOfMonth() const nothrow + { + immutable hnsecs = adjTime; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + + auto date = Date(cast(int)days + 1).endOfMonth; + auto newDays = date.dayOfGregorianCal - 1; + long theTimeHNSecs; + + if(newDays < 0) + { + theTimeHNSecs = -1; + ++newDays; + } + else + theTimeHNSecs = convert!("days", "hnsecs")(1) - 1; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(newDays); + + auto retval = SysTime(this._stdTime, this._timezone.get); + retval.adjTime = newDaysHNSecs + theTimeHNSecs; + + return retval; + } + + unittest + { + //Test A.D. + assertPred!"=="(SysTime(Date(1999, 1, 1)).endOfMonth, SysTime(DateTime(1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 2, 1)).endOfMonth, SysTime(DateTime(1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(2000, 2, 1)).endOfMonth, SysTime(DateTime(2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 3, 1)).endOfMonth, SysTime(DateTime(1999, 3, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 4, 1)).endOfMonth, SysTime(DateTime(1999, 4, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 5, 1)).endOfMonth, SysTime(DateTime(1999, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 6, 1)).endOfMonth, SysTime(DateTime(1999, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 7, 1)).endOfMonth, SysTime(DateTime(1999, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 8, 1)).endOfMonth, SysTime(DateTime(1999, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 9, 1)).endOfMonth, SysTime(DateTime(1999, 9, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 10, 1)).endOfMonth, SysTime(DateTime(1999, 10, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 11, 1)).endOfMonth, SysTime(DateTime(1999, 11, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(1999, 12, 1)).endOfMonth, SysTime(DateTime(1999, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + //Test B.C. + assertPred!"=="(SysTime(Date(-1999, 1, 1)).endOfMonth, SysTime(DateTime(-1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 2, 1)).endOfMonth, SysTime(DateTime(-1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-2000, 2, 1)).endOfMonth, SysTime(DateTime(-2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 3, 1)).endOfMonth, SysTime(DateTime(-1999, 3, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 4, 1)).endOfMonth, SysTime(DateTime(-1999, 4, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 5, 1)).endOfMonth, SysTime(DateTime(-1999, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 6, 1)).endOfMonth, SysTime(DateTime(-1999, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 7, 1)).endOfMonth, SysTime(DateTime(-1999, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 8, 1)).endOfMonth, SysTime(DateTime(-1999, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 9, 1)).endOfMonth, SysTime(DateTime(-1999, 9, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 10, 1)).endOfMonth, SysTime(DateTime(-1999, 10, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 11, 1)).endOfMonth, SysTime(DateTime(-1999, 11, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assertPred!"=="(SysTime(Date(-1999, 12, 1)).endOfMonth, SysTime(DateTime(-1999, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + 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.endOfMonth)); + //static assert(__traits(compiles, ist.endOfMonth)); + + //Verify Examples. + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), FracSec.from!"msecs"(24)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), FracSec.from!"usecs"(5203)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), FracSec.from!"hnsecs"(12345)).endOfMonth == SysTime(DateTime(2000, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + + /++ + The last day in the month that this SysTime is in. + + Examples: +-------------------- +assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonthDay == 31); +assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).endOfMonthDay == 28); +assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).endOfMonthDay == 29); +assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).endOfMonthDay == 30); +-------------------- + +/ + @property ubyte endOfMonthDay() const nothrow + { + return Date(dayOfGregorianCal).endOfMonthDay; + } + + unittest + { + //Test A.D. + assertPred!"=="(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).endOfMonthDay, 28); + assertPred!"=="(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).endOfMonthDay, 29); + assertPred!"=="(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).endOfMonthDay, 31); + + //Test B.C. + assertPred!"=="(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).endOfMonthDay, 28); + assertPred!"=="(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).endOfMonthDay, 29); + assertPred!"=="(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).endOfMonthDay, 31); + assertPred!"=="(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).endOfMonthDay, 30); + assertPred!"=="(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).endOfMonthDay, 31); + + 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.endOfMonthDay)); + //static assert(__traits(compiles, ist.endOfMonthDay)); + + //Verify Examples. + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonthDay == 31); + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).endOfMonthDay == 28); + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).endOfMonthDay == 29); + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).endOfMonthDay == 30); + } + + + /++ + Whether the current year is a date in A.D. + + Examples: +-------------------- +assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); +assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); +assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); +assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); +-------------------- + +/ + @property bool isAD() const nothrow + { + return adjTime >= 0; + } + + unittest + { + assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); + assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); + + 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.isAD)); + //static assert(__traits(compiles, ist.isAD)); + + //Verify Examples. + assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); + assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); + } + + + /++ + The julian day for this SysTime at the given time. For example, prior + to noon, 1996-03-31 would be the julian day number 2_450_173, so this + function returns 2_450_173, while from noon onward, it the julian day + number would be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() const nothrow + { + immutable jd = dayOfGregorianCal + 1_721_425; + + return hour < 12 ? jd - 1 : jd; + } + + unittest + { + assertPred!"=="(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay, -1); + assertPred!"=="(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay, 0); + + assertPred!"=="(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay, 1_721_424); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay, 1_721_425); + + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay, 1_721_425); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay, 1_721_426); + + assertPred!"=="(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay, 2_299_160); + assertPred!"=="(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay, 2_299_161); + + assertPred!"=="(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay, 2_400_000); + assertPred!"=="(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay, 2_400_001); + + assertPred!"=="(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay, 2_444_973); + assertPred!"=="(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay, 2_444_974); + + assertPred!"=="(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay, 2_450_173); + assertPred!"=="(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay, 2_450_174); + + assertPred!"=="(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay, 2_455_432); + assertPred!"=="(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay, 2_455_433); + + 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.julianDay)); + //static assert(__traits(compiles, ist.julianDay)); + } + + + /++ + The modified julian day for any time on this Date (since, the modified + julian day changes at midnight). + +/ + @property long modJulianDay() const nothrow + { + return (dayOfGregorianCal + 1_721_425) - 2_400_001; + } + + unittest + { + assertPred!"=="(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay, 0); + assertPred!"=="(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay, 0); + + assertPred!"=="(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay, 55_432); + assertPred!"=="(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay, 55_432); + + 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.modJulianDay)); + //static assert(__traits(compiles, ist.modJulianDay)); + } + + + /++ + Returns a Date equivalent to this SysTime. + +/ + Date opCast(T)() const nothrow + if(is(Unqual!T == Date)) + { + return Date(dayOfGregorianCal); + } + + unittest + { + assertPred!"=="(cast(Date)SysTime(Date(1999, 7, 6)), Date(1999, 7, 6)); + assertPred!"=="(cast(Date)SysTime(Date(2000, 12, 31)), Date(2000, 12, 31)); + assertPred!"=="(cast(Date)SysTime(Date(2001, 1, 1)), Date(2001, 1, 1)); + + assertPred!"=="(cast(Date)SysTime(DateTime(1999, 7, 6, 12, 10, 9)), Date(1999, 7, 6)); + assertPred!"=="(cast(Date)SysTime(DateTime(2000, 12, 31, 13, 11, 10)), Date(2000, 12, 31)); + assertPred!"=="(cast(Date)SysTime(DateTime(2001, 1, 1, 14, 12, 11)), Date(2001, 1, 1)); + + assertPred!"=="(cast(Date)SysTime(Date(-1999, 7, 6)), Date(-1999, 7, 6)); + assertPred!"=="(cast(Date)SysTime(Date(-2000, 12, 31)), Date(-2000, 12, 31)); + assertPred!"=="(cast(Date)SysTime(Date(-2001, 1, 1)), Date(-2001, 1, 1)); + + assertPred!"=="(cast(Date)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)), Date(-1999, 7, 6)); + assertPred!"=="(cast(Date)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)), Date(-2000, 12, 31)); + assertPred!"=="(cast(Date)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)), Date(-2001, 1, 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, cast(Date)cst)); + //static assert(__traits(compiles, cast(Date)ist)); + } + + + /++ + Returns a DateTime equivalent to this SysTime. + +/ + DateTime opCast(T)() const nothrow + if(is(Unqual!T == DateTime)) + { + try + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + return DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); + } + catch(Exception e) + assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); + } + + unittest + { + assertPred!"=="(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22)), DateTime(1, 1, 6, 7, 12, 22)); + assertPred!"=="(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22), FracSec.from!"msecs"(22)), DateTime(1, 1, 6, 7, 12, 22)); + assertPred!"=="(cast(DateTime)SysTime(Date(1999, 7, 6)), DateTime(1999, 7, 6, 0, 0, 0)); + assertPred!"=="(cast(DateTime)SysTime(Date(2000, 12, 31)), DateTime(2000, 12, 31, 0, 0, 0)); + assertPred!"=="(cast(DateTime)SysTime(Date(2001, 1, 1)), DateTime(2001, 1, 1, 0, 0, 0)); + + assertPred!"=="(cast(DateTime)SysTime(DateTime(1999, 7, 6, 12, 10, 9)), DateTime(1999, 7, 6, 12, 10, 9)); + assertPred!"=="(cast(DateTime)SysTime(DateTime(2000, 12, 31, 13, 11, 10)), DateTime(2000, 12, 31, 13, 11, 10)); + assertPred!"=="(cast(DateTime)SysTime(DateTime(2001, 1, 1, 14, 12, 11)), DateTime(2001, 1, 1, 14, 12, 11)); + + assertPred!"=="(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22)), DateTime(-1, 1, 6, 7, 12, 22)); + assertPred!"=="(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22), FracSec.from!"msecs"(22)), DateTime(-1, 1, 6, 7, 12, 22)); + assertPred!"=="(cast(DateTime)SysTime(Date(-1999, 7, 6)), DateTime(-1999, 7, 6, 0, 0, 0)); + assertPred!"=="(cast(DateTime)SysTime(Date(-2000, 12, 31)), DateTime(-2000, 12, 31, 0, 0, 0)); + assertPred!"=="(cast(DateTime)SysTime(Date(-2001, 1, 1)), DateTime(-2001, 1, 1, 0, 0, 0)); + + assertPred!"=="(cast(DateTime)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)), DateTime(-1999, 7, 6, 12, 10, 9)); + assertPred!"=="(cast(DateTime)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)), DateTime(-2000, 12, 31, 13, 11, 10)); + assertPred!"=="(cast(DateTime)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)), DateTime(-2001, 1, 1, 14, 12, 11)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(DateTime)cst)); + //static assert(__traits(compiles, cast(DateTime)ist)); + } + + + /++ + Returns a TimeOfDay equivalent to this SysTime. + +/ + TimeOfDay opCast(T)() const nothrow + if(is(Unqual!T == TimeOfDay)) + { + try + { + auto hnsecs = adjTime; + hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); + + if(hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + return TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second); + } + catch(Exception e) + assert(0, "TimeOfDay's constructor threw."); + } + + unittest + { + assertPred!"=="(cast(TimeOfDay)SysTime(Date(1999, 7, 6)), TimeOfDay(0, 0, 0)); + assertPred!"=="(cast(TimeOfDay)SysTime(Date(2000, 12, 31)), TimeOfDay(0, 0, 0)); + assertPred!"=="(cast(TimeOfDay)SysTime(Date(2001, 1, 1)), TimeOfDay(0, 0, 0)); + + assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(1999, 7, 6, 12, 10, 9)), TimeOfDay(12, 10, 9)); + assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(2000, 12, 31, 13, 11, 10)), TimeOfDay(13, 11, 10)); + assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(2001, 1, 1, 14, 12, 11)), TimeOfDay(14, 12, 11)); + + assertPred!"=="(cast(TimeOfDay)SysTime(Date(-1999, 7, 6)), TimeOfDay(0, 0, 0)); + assertPred!"=="(cast(TimeOfDay)SysTime(Date(-2000, 12, 31)), TimeOfDay(0, 0, 0)); + assertPred!"=="(cast(TimeOfDay)SysTime(Date(-2001, 1, 1)), TimeOfDay(0, 0, 0)); + + assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)), TimeOfDay(12, 10, 9)); + assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)), TimeOfDay(13, 11, 10)); + assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)), TimeOfDay(14, 12, 11)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); + } + + + //Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed. + //This allows assignment from const(SysTime) to SysTime. + //It may be a good idea to keep it though, since casting from a type to itself + //should be allowed, and it doesn't work without this opCast() since opCast() + //has already been defined for other types. + SysTime opCast(T)() const pure nothrow + if(is(Unqual!T == SysTime)) + { + return SysTime(_stdTime, _timezone.get); + } + + + /++ + Converts this SysTime to a string with the format YYYYMMDDTHHMMSS.FFFFFFFTZ + (where F is fractional seconds and TZ is time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be hnsecs), + but only has as many as are necessary to hold the correct value (so no + trailing zeroes), and if there are no fractional seconds, then there is + no decimal point. + + If this SysTime's time zone is LocalTime, then TZ is empty. If the time zone + is UTC, then it is "Z". Otherwise, it is the offset from UTC (e.g. +1:00 or -7:00). + Note that the offset from UTC is $(I not) enough to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + + Examples: +-------------------- +assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == + "20100704T070612"); +assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(24)).toISOString() == + "19981225T021500.024"); +assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == + "00000105T230959"); +assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), FracSec.from!"hnsecs"(520920)).toISOString() == + "-00040105T000002.052092"); +-------------------- + +/ + string toISOString() const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); + auto fracSecStr = fracSecToISOString(cast(int)hnsecs); + + if(_timezone.get is LocalTime()) + return dateTime.toISOString() ~ fracSecToISOString(cast(int)hnsecs); + + if(_timezone.get is UTC()) + return dateTime.toISOString() ~ fracSecToISOString(cast(int)hnsecs) ~ "Z"; + + immutable utcOffset = cast(int)convert!("hnsecs", "minutes")(stdTime - adjustedTime); + + return dateTime.toISOString() ~ fracSecToISOString(cast(int)hnsecs) ~ SimpleTimeZone.toISOString(utcOffset); + } + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(SysTime(DateTime.init, UTC()).toISOString(), "00010101T000000Z"); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toISOString(), "00010101T000000.0000001Z"); + + assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString(), "00091204T000000"); + assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString(), "00991204T050612"); + assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString(), "09991204T134459"); + assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString(), "99990704T235959"); + assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString(), "+100001020T010101"); + + assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOString(), "00091204T000000.042"); + assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOString(), "00991204T050612.1"); + assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOString(), "09991204T134459.04502"); + assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOString(), "99990704T235959.0000012"); + assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOString(), "+100001020T010101.050789"); + + //Test B.C. + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toISOString(), "00001231T235959.9999999Z"); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toISOString(), "00001231T235959.0000001Z"); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString(), "00001231T235959Z"); + + assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString(), "00001204T001204"); + assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString(), "-00091204T000000"); + assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString(), "-00991204T050612"); + assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString(), "-09991204T134459"); + assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString(), "-99990704T235959"); + assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString(), "-100001020T010101"); + + assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toISOString(), "00001204T000000.007"); + assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOString(), "-00091204T000000.042"); + assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOString(), "-00991204T050612.1"); + assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOString(), "-09991204T134459.04502"); + assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOString(), "-99990704T235959.0000012"); + assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOString(), "-100001020T010101.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); + + //Verify Examples. + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == "20100704T070612"); + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(24)).toISOString() == "19981225T021500.024"); + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == "00000105T230959"); + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), FracSec.from!"hnsecs"(520920)).toISOString() == "-00040105T000002.052092"); + } + + + + /++ + Converts this SysTime to a string with the format YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ + (where F is fractional seconds and TZ is time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be hnsecs), + but only has as many as are necessary to hold the correct value (so no + trailing zeroes), and if there are no fractional seconds, then there is + no decimal point. + + If this SysTime's time zone is LocalTime, then TZ is empty. If the time + zone is UTC, then it is "Z". Otherwise, it is the offset from UTC + (e.g. +1:00 or -7:00). Note that the offset from UTC is $(I not) enough + to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + + Examples: +-------------------- +assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtendedString() == + "2010-07-04T07:06:12"); +assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(24)).toISOExtendedString() == + "1998-12-25T02:15:00.024"); +assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtendedString() == + "0000-01-05T23:09:59"); +assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), FracSec.from!"hnsecs"(520920)).toISOExtendedString() == + "-0004-01-05T00:00:02.052092"); +-------------------- + +/ + string toISOExtendedString() const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); + auto fracSecStr = fracSecToISOString(cast(int)hnsecs); + + if(_timezone.get is LocalTime()) + return dateTime.toISOExtendedString() ~ fracSecToISOString(cast(int)hnsecs); + + if(_timezone.get is UTC()) + return dateTime.toISOExtendedString() ~ fracSecToISOString(cast(int)hnsecs) ~ "Z"; + + immutable utcOffset = cast(int)convert!("hnsecs", "minutes")(stdTime - adjustedTime); + + return dateTime.toISOExtendedString() ~ fracSecToISOString(cast(int)hnsecs) ~ SimpleTimeZone.toISOString(utcOffset); + } + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(SysTime(DateTime.init, UTC()).toISOExtendedString(), "0001-01-01T00:00:00Z"); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toISOExtendedString(), "0001-01-01T00:00:00.0000001Z"); + + assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtendedString(), "0009-12-04T00:00:00"); + assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtendedString(), "0099-12-04T05:06:12"); + assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtendedString(), "0999-12-04T13:44:59"); + assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtendedString(), "9999-07-04T23:59:59"); + assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtendedString(), "+10000-10-20T01:01:01"); + + assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOExtendedString(), "0009-12-04T00:00:00.042"); + assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOExtendedString(), "0099-12-04T05:06:12.1"); + assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOExtendedString(), "0999-12-04T13:44:59.04502"); + assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOExtendedString(), "9999-07-04T23:59:59.0000012"); + assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOExtendedString(), "+10000-10-20T01:01:01.050789"); + + //Test B.C. + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toISOExtendedString(), "0000-12-31T23:59:59.9999999Z"); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toISOExtendedString(), "0000-12-31T23:59:59.0000001Z"); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtendedString(), "0000-12-31T23:59:59Z"); + + assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtendedString(), "0000-12-04T00:12:04"); + assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtendedString(), "-0009-12-04T00:00:00"); + assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtendedString(), "-0099-12-04T05:06:12"); + assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtendedString(), "-0999-12-04T13:44:59"); + assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtendedString(), "-9999-07-04T23:59:59"); + assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtendedString(), "-10000-10-20T01:01:01"); + + assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toISOExtendedString(), "0000-12-04T00:00:00.007"); + assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOExtendedString(), "-0009-12-04T00:00:00.042"); + assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOExtendedString(), "-0099-12-04T05:06:12.1"); + assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOExtendedString(), "-0999-12-04T13:44:59.04502"); + assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOExtendedString(), "-9999-07-04T23:59:59.0000012"); + assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOExtendedString(), "-10000-10-20T01:01:01.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); + + //Verify Examples. + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtendedString() == "2010-07-04T07:06:12"); + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(24)).toISOExtendedString() == "1998-12-25T02:15:00.024"); + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtendedString() == "0000-01-05T23:09:59"); + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), FracSec.from!"hnsecs"(520920)).toISOExtendedString() == "-0004-01-05T00:00:02.052092"); + } + + /++ + Converts this SysTime to a string with the format YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ + (where F is fractional seconds and TZ is time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be hnsecs), + but only has as many as are necessary to hold the correct value (so no + trailing zeroes), and if there are no fractional seconds, then there is + no decimal point. + + If this SysTime's time zone is LocalTime, then TZ is empty. If the time + zone is UTC, then it is "Z". Otherwise, it is the offset from UTC + (e.g. +1:00 or -7:00). Note that the offset from UTC is $(I not) enough + to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + + Examples: +-------------------- +assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); +assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(24)).toSimpleString() == + "1998-Dec-25 02:15:00.024"); +assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); +assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), FracSec.from!"hnsecs"(520920)).toSimpleString() == + "-0004-Jan-05 00:00:02.052092"); +-------------------- + +/ + string toSimpleString() const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if(hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int)days), TimeOfDay(cast(int)hour, cast(int)minute, cast(int)second)); + auto fracSecStr = fracSecToISOString(cast(int)hnsecs); + + if(_timezone.get is LocalTime()) + return dateTime.toSimpleString() ~ fracSecToISOString(cast(int)hnsecs); + + if(_timezone.get is UTC()) + return dateTime.toSimpleString() ~ fracSecToISOString(cast(int)hnsecs) ~ "Z"; + + immutable utcOffset = cast(int)convert!("hnsecs", "minutes")(stdTime - adjustedTime); + + return dateTime.toSimpleString() ~ fracSecToISOString(cast(int)hnsecs) ~ SimpleTimeZone.toISOString(utcOffset); + } + catch(Exception e) + assert(0, "format() threw."); + } + + unittest + { + //Test A.D. + assertPred!"=="(SysTime(DateTime.init, UTC()).toString(), "0001-Jan-01 00:00:00Z"); + assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toString(), "0001-Jan-01 00:00:00.0000001Z"); + + assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString(), "0009-Dec-04 00:00:00"); + assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString(), "0099-Dec-04 05:06:12"); + assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString(), "0999-Dec-04 13:44:59"); + assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString(), "9999-Jul-04 23:59:59"); + assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString(), "+10000-Oct-20 01:01:01"); + + assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toSimpleString(), "0009-Dec-04 00:00:00.042"); + assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toSimpleString(), "0099-Dec-04 05:06:12.1"); + assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toSimpleString(), "0999-Dec-04 13:44:59.04502"); + assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toSimpleString(), "9999-Jul-04 23:59:59.0000012"); + assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toSimpleString(), "+10000-Oct-20 01:01:01.050789"); + + //Test B.C. + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toSimpleString(), "0000-Dec-31 23:59:59.9999999Z"); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toSimpleString(), "0000-Dec-31 23:59:59.0000001Z"); + assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString(), "0000-Dec-31 23:59:59Z"); + + assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString(), "0000-Dec-04 00:12:04"); + assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString(), "-0009-Dec-04 00:00:00"); + assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString(), "-0099-Dec-04 05:06:12"); + assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString(), "-0999-Dec-04 13:44:59"); + assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString(), "-9999-Jul-04 23:59:59"); + assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString(), "-10000-Oct-20 01:01:01"); + + assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toSimpleString(), "0000-Dec-04 00:00:00.007"); + assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toSimpleString(), "-0009-Dec-04 00:00:00.042"); + assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toSimpleString(), "-0099-Dec-04 05:06:12.1"); + assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toSimpleString(), "-0999-Dec-04 13:44:59.04502"); + assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toSimpleString(), "-9999-Jul-04 23:59:59.0000012"); + assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toSimpleString(), "-10000-Oct-20 01:01:01.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); + + //Verify Examples. + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(24)).toSimpleString() == "1998-Dec-25 02:15:00.024"); + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == "0000-Jan-05 23:09:59"); + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), FracSec.from!"hnsecs"(520920)).toSimpleString() == "-0004-Jan-05 00:00:02.052092"); + } + + + /+ + Converts this SysTime to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return toSimpleString(); + } + + /++ + Converts this SysTime to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return toSimpleString(); + } + + 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)); + static assert(__traits(compiles, st.toString())); + static assert(__traits(compiles, cst.toString())); + //static assert(__traits(compiles, ist.toString())); + } + + + /++ + Creates a SysTime from a string with the format YYYYMMDDTHHMMSS.FFFFFFFTZ + (where F is fractional seconds is time zone). Whitespace is stripped from + the given string. + + The exact format is exactly as described in toISOString() except that + trailing zeroes are permitted - including having fractional seconds with + all zeroes. However, a decimal point with nothing following it is invalid. + + If there is no time zone in the string, then LocalTime is used. If the + time zone is "Z", then UTC is used. Otherwise, a SimpleTimeZone which + corresponds to the given offset from UTC is used. If you wish the + returned SysTime to be a particular time zone, then pass in that time + zone and the SysTime to be returned will be converted to that time zone + (though it will still be read in as whatever time zone is in its string). + + The accepted formats for time zone offsets are +H, -H, +HH, -HH, +H:MM, + -H:MM, +HH:MM, and -HH:MM. + + Params: + isoString = A string formatted in the ISO format for dates and times. + tz = The time zone to convert the given time to (no conversion + occurs if null). + + Throws: + DateTimeException if the given string is not in the ISO format or if + the resulting SysTime would not be valid. + + Examples: +-------------------- +assert(SysTime.fromISOString("20100704T070612") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); +assert(SysTime.fromISOString("19981225T021500.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); +assert(SysTime.fromISOString("00000105T230959.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); +assert(SysTime.fromISOString("-00040105T000002") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); +assert(SysTime.fromISOString(" 20100704T070612 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + +assert(SysTime.fromISOString("20100704T070612Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); +assert(SysTime.fromISOString("20100704T070612-8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(-480))); +assert(SysTime.fromISOString("20100704T070612+8:00") == + SysTime(DateTime(2010, 7, 3, 7, 6, 12), new SimpleTimeZone(480))); +-------------------- + +/ + static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) + if(isSomeString!S) + { + auto dstr = to!dstring(strip(isoString)); + immutable skipFirst = dstr.startsWith("+", "-") != 0; + + auto found = (skipFirst ? dstr[1..$] : dstr).find(".", "Z", "+", "-"); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if(found[1] != 0) + { + if(found[1] == 1) + { + auto foundTZ = found[0].find("Z", "+", "-"); + + if(foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromISOString(dateTimeStr); + auto fracSec = fracSecFromISOString(fracSecStr); + DTRebindable!(immutable TimeZone) parsedZone; + + if(zoneStr.empty) + parsedZone = LocalTime(); + else if(zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone.get); + + if(tz !is null) + retval.timezone = tz; + + return retval; + } + catch(DateTimeException dte) + throw new DateTimeException(format("Invalid ISO String: %s", isoString)); + } + + unittest + { + assertThrown!DateTimeException(SysTime.fromISOString("")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.A")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.Z")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-1:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:0")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-24.00")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+24.00")); + + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04T00:00:00.")); + + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00.")); + + assertThrown!DateTimeException(SysTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Dec-22 17:22:01")); + + assertPred!"=="(SysTime.fromISOString("20101222T172201"), SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + assertPred!"=="(SysTime.fromISOString("19990706T123033"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOString("-19990706T123033"), SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOString("+019990706T123033"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOString("19990706T123033 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOString(" 19990706T123033"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOString(" 19990706T123033 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + assertPred!"=="(SysTime.fromISOString("19070707T121212.0"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assertPred!"=="(SysTime.fromISOString("19070707T121212.0000000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assertPred!"=="(SysTime.fromISOString("19070707T121212.0000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); + assertPred!"=="(SysTime.fromISOString("19070707T121212.000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assertPred!"=="(SysTime.fromISOString("19070707T121212.0000010"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assertPred!"=="(SysTime.fromISOString("19070707T121212.001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + assertPred!"=="(SysTime.fromISOString("19070707T121212.0010000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + + assertPred!"=="(SysTime.fromISOString("20101222T172201Z"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + assertPred!"=="(SysTime.fromISOString("20101222T172201-1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201-1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201-1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-90))); + assertPred!"=="(SysTime.fromISOString("20101222T172201-8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-480))); + assertPred!"=="(SysTime.fromISOString("20101222T172201+1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201+1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201+1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(90))); + assertPred!"=="(SysTime.fromISOString("20101222T172201+8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(480))); + + assertPred!"=="(SysTime.fromISOString("20101103T065106.57159Z"), SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); + + assertPred!"=="(SysTime.fromISOString("20101222T172201.23412Z"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); + assertPred!"=="(SysTime.fromISOString("20101222T172201.23112-1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.45-1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.1-1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), new SimpleTimeZone(-90))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.55-8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), new SimpleTimeZone(-480))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.1234567+1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.0+1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.0000000+1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), new SimpleTimeZone(90))); + assertPred!"=="(SysTime.fromISOString("20101222T172201.45+8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), new SimpleTimeZone(480))); + + //Verify Examples. + assert(SysTime.fromISOString("20100704T070612") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + assert(SysTime.fromISOString("19981225T021500.007") == SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); + assert(SysTime.fromISOString("00000105T230959.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); + assert(SysTime.fromISOString("-00040105T000002") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + assert(SysTime.fromISOString(" 20100704T070612 ") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOString("20100704T070612Z") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + assert(SysTime.fromISOString("20100704T070612-8:00") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(-480))); + assert(SysTime.fromISOString("20100704T070612+8:00") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(480))); + } + + + /++ + Creates a SysTime from a string with the format YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ + (where F is fractional seconds is time zone). Whitespace is stripped from the + given string. + + The exact format is exactly as described in toISOExtendedString() except + that trailing zeroes are permitted - including having fractional seconds + with all zeroes. However, a decimal point with nothing following it is + invalid. + + If there is no time zone in the string, then LocalTime is used. If the + time zone is "Z", then UTC is used. Otherwise, a SimpleTimeZone which + corresponds to the given offset from UTC is used. If you wish the + returned SysTime to be a particular time zone, then pass in that time + zone and the SysTime to be returned will be converted to that time zone + (though it will still be read in as whatever time zone is in its string). + + The accepted formats for time zone offsets are +H, -H, +HH, -HH, +H:MM, + -H:MM, +HH:MM, and -HH:MM. + + Params: + isoString = A string formatted in the ISO Extended format for dates + and times. + tz = The time zone to convert the given time to (no conversion + occurs if null). + + Throws: + DateTimeException if the given string is not in the ISO format or if + the resulting SysTime would not be valid. + + Examples: +-------------------- +assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); +assert(SysTime.fromISOExtendedString("1998-12-25T02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); +assert(SysTime.fromISOExtendedString("0000-01-05T23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); +assert(SysTime.fromISOExtendedString("-0004-01-05T00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); +assert(SysTime.fromISOExtendedString(" 2010-07-04T07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + +assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); +assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12-8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(-480))); +assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12+8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(480))); +-------------------- + +/ + static SysTime fromISOExtendedString(S)(in S isoExtString, immutable TimeZone tz = null) + if(isSomeString!(S)) + { + auto dstr = to!dstring(strip(isoExtString)); + + auto tIndex = dstr.indexOf("T"); + enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto found = dstr[tIndex + 1 .. $].find(".", "Z", "+", "-"); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if(found[1] != 0) + { + if(found[1] == 1) + { + auto foundTZ = found[0].find("Z", "+", "-"); + + if(foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromISOExtendedString(dateTimeStr); + auto fracSec = fracSecFromISOString(fracSecStr); + DTRebindable!(immutable TimeZone) parsedZone; + + if(zoneStr.empty) + parsedZone = LocalTime(); + else if(zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone.get); + + if(tz !is null) + retval.timezone = tz; + + return retval; + } + catch(DateTimeException dte) + throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); + } + + unittest + { + assertThrown!DateTimeException(SysTime.fromISOExtendedString("")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("20100704000000")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("20100704 000000")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("20100704t000000")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("20100704T000000.")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("20100704T000000.0")); + + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07:0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00.A")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00.Z")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00+")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00-")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00:")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00-:")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00+:")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00-1:")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00+1:")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00+1:0")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00-24.00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-07-04T00:00:00+24.00")); + + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(SysTime.fromISOExtendedString("20101222T172201")); + assertThrown!DateTimeException(SysTime.fromISOExtendedString("2010-Dec-22 17:22:01")); + + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01"), SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + assertPred!"=="(SysTime.fromISOExtendedString("1999-07-06T12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOExtendedString("-1999-07-06T12:30:33"), SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOExtendedString("+01999-07-06T12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOExtendedString("1999-07-06T12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOExtendedString(" 1999-07-06T12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromISOExtendedString(" 1999-07-06T12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.0"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.0000000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.0000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.0000010"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + assertPred!"=="(SysTime.fromISOExtendedString("1907-07-07T12:12:12.0010000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01Z"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01-1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01-1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01-1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-90))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01-8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-480))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01+1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01+1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01+1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(90))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01+8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(480))); + + assertPred!"=="(SysTime.fromISOExtendedString("2010-11-03T06:51:06.57159Z"), SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); + + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.23412Z"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.23112-1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.45-1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.1-1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), new SimpleTimeZone(-90))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.55-8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), new SimpleTimeZone(-480))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.1234567+1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.0+1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.0000000+1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), new SimpleTimeZone(90))); + assertPred!"=="(SysTime.fromISOExtendedString("2010-12-22T17:22:01.45+8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), new SimpleTimeZone(480))); + + //Verify Examples. + assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + assert(SysTime.fromISOExtendedString("1998-12-25T02:15:00.007") == SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); + assert(SysTime.fromISOExtendedString("0000-01-05T23:09:59.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); + assert(SysTime.fromISOExtendedString("-0004-01-05T00:00:02") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + assert(SysTime.fromISOExtendedString(" 2010-07-04T07:06:12 ") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12Z") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12-8:00") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(-480))); + assert(SysTime.fromISOExtendedString("2010-07-04T07:06:12+8:00") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(480))); + } + + + /++ + Creates a SysTime from a string with the format YYYY-MM-DD HH:MM:SS.FFFFFFFTZ + (where F is fractional seconds is time zone). Whitespace is stripped from the + given string. + + The exact format is exactly as described in toSimpleString() except that + trailing zeroes are permitted - including having fractional seconds with + all zeroes. However, a decimal point with nothing following it is invalid. + + If there is no time zone in the string, then LocalTime is used. If the + time zone is "Z", then UTC is used. Otherwise, a SimpleTimeZone which + corresponds to the given offset from UTC is used. If you wish the + returned SysTime to be a particular time zone, then pass in that time + zone and the SysTime to be returned will be converted to that time zone + (though it will still be read in as whatever time zone is in its string). + + The accepted formats for time zone offsets are +H, -H, +HH, -HH, +H:MM, + -H:MM, +HH:MM, and -HH:MM. + + + Params: + simpleString = A string formatted in the way that toSimpleString() + formats dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + DateTimeException if the given string is not in the ISO format or if + the resulting SysTime would not be valid. + + Examples: +-------------------- +assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); +assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); +assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); +assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); +assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + +assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); +assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(-480))); +assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(480))); +-------------------- + +/ + static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) + if(isSomeString!(S)) + { + auto dstr = to!dstring(strip(simpleString)); + + auto spaceIndex = dstr.indexOf(" "); + enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString))); + + auto found = dstr[spaceIndex + 1 .. $].find(".", "Z", "+", "-"); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if(found[1] != 0) + { + if(found[1] == 1) + { + auto foundTZ = found[0].find("Z", "+", "-"); + + if(foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromSimpleString(dateTimeStr); + auto fracSec = fracSecFromISOString(fracSecStr); + DTRebindable!(immutable TimeZone) parsedZone; + + if(zoneStr.empty) + parsedZone = LocalTime(); + else if(zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone.get); + + if(tz !is null) + retval.timezone = tz; + + return retval; + } + catch(DateTimeException dte) + throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); + } + + unittest + { + assertThrown!DateTimeException(SysTime.fromSimpleString("")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704 000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704t000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.0")); + + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.A")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.Z")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-1:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:0")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-24.00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+24.00")); + + assertThrown!DateTimeException(SysTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-12-22T172201")); + + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01"), SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + assertPred!"=="(SysTime.fromSimpleString("1999-Jul-06 12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromSimpleString("-1999-Jul-06 12:30:33"), SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromSimpleString("+01999-Jul-06 12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromSimpleString("1999-Jul-06 12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assertPred!"=="(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000010"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0010000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01Z"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-90))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(-480))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(90))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), new SimpleTimeZone(480))); + + assertPred!"=="(SysTime.fromSimpleString("2010-Nov-03 06:51:06.57159Z"), SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); + + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23412Z"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23112-1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45-1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), new SimpleTimeZone(-60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1-1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), new SimpleTimeZone(-90))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.55-8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), new SimpleTimeZone(-480))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1234567+1:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0+1"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), new SimpleTimeZone(60))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0000000+1:30"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), new SimpleTimeZone(90))); + assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45+8:00"), SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), new SimpleTimeZone(480))); + + //Verify Examples. + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); + assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); + assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-8:00") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(-480))); + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), new SimpleTimeZone(480))); + } + + + //TODO Add function which takes a user-specified time format and produces a SysTime + + //TODO Add function which takes pretty much any time-string and produces a SysTime. + // Obviously, it will be less efficient, and it probably won't manage _every_ + // possible date format, but a smart conversion function would be nice. + + + /++ + Returns the SysTime farthest in the past which is representable by SysTime. + + The SysTime which is returned is in UTC. + +/ + @property static SysTime min() pure nothrow + { + return SysTime(long.min, UTC()); + } + + unittest + { + assert(SysTime.min.year < 0); + assert(SysTime.min < SysTime.max); + } + + + /++ + Returns the SysTime farthest in the future which is representable by SysTime. + + The SysTime which is returned is in UTC. + +/ + @property static SysTime max() pure nothrow + { + return SysTime(long.max, UTC()); + } + + unittest + { + assert(SysTime.max.year > 0); + assert(SysTime.max > SysTime.min); + } + + +private: + + /++ + Returns stdTime converted to SysTime's time zone. + +/ + @property long adjTime() const nothrow + { + return _timezone.utcToTZ(_stdTime); + } + + + /++ + Converts the given hnsecs from SysTime's time zone to std time. + +/ + @property void adjTime(long adjTime) nothrow + { + _stdTime = _timezone.tzToUTC(adjTime); + } + + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058 + /+ + invariant() + { + assert(_timezone.get !is null, "Invariant Failure: timezone is null. Were you foolish enough to use SysTime.init? (since timezone for SysTime.init can't be set at compile time)."); + } + +/ + + + long _stdTime; + DTRebindable!(immutable TimeZone) _timezone; +} + + +//============================================================================== +// Section with intervals. +//============================================================================== + +/++ + Represents an interval of time. + + An $(D Interval) has a begin point and an end point. The interval of time is + therefore the time starting at the begin point up to, but not including, the + end point. e.g. + + $(TABLE + $(TR $(TD [January 5th, 2010 - March 10th, 2010$(RPAREN))) + $(TR $(TD [05:00:30 - 12:00:00$(RPAREN))) + $(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN))) + ) + + A range can be obtained from an $(D Interval), allowing you to iterate over + that interval, with the exact time points which are iterated over depending + on the function which generates the range. + +/ +struct Interval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + end = The time point which ends (but is not included in) the interval. + + Throws: + DateTimeException if end is before begin. + + Examples: +-------------------- +Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); +-------------------- + +/ + this(U)(in TP begin, in U end) pure + if(is(Unqual!TP == Unqual!U)) + { + if(!_valid(begin, end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + + _begin = cast(TP)begin; + _end = cast(TP)end; + } + + + /++ + Params: + begin = The time point which begins the interval. + duration = The duration from the begin point to the end point. + + Throws: + DateTimeException if the resulting end is before begin. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Dur.years(3)) == Interval!Date(Date(1996, 1, 2), Date(1999, 1, 2))); +-------------------- + +/ + this(D)(in TP begin, in D duration) pure + if(__traits(compiles, begin + duration)) + { + _begin = cast(TP)begin; + _end = begin + duration; + + if(!_valid(_begin, _end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + } + + + /++ + Params: + rhs = The Interval to assign to this one. + +/ + /+ref+/ Interval opAssign(const ref Interval rhs) pure nothrow + { + _begin = cast(TP)rhs._begin; + _end = cast(TP)rhs._end; + + return this; + } + + + /++ + Params: + rhs = The Interval to assign to this one. + +/ + /+ref+/ Interval opAssign(Interval rhs) pure nothrow + { + _begin = cast(TP)rhs._begin; + _end = cast(TP)rhs._end; + + return this; + } + + + /++ + The begin point of the interval. It is included in the interval. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); +-------------------- + +/ + @property TP begin() const pure nothrow + { + return cast(TP)_begin; + } + + + /++ + The begin point of the interval. It is included in the interval. + + Params: + timePoint = The time point to set begin to. + + Throws: + DateTimeException if the resulting interval would be invalid. + +/ + @property void begin(TP timePoint) pure + { + if(!_valid(timePoint, _end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + + _begin = timePoint; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); +-------------------- + +/ + @property TP end() const pure nothrow + { + return cast(TP)_end; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Params: + timePoint = The time point to set end to. + + Throws: + DateTimeException if the resulting interval would be invalid. + +/ + @property void end(TP timePoint) pure + { + if(!_valid(_begin, timePoint)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + + _end = timePoint; + } + + + /++ + Returns the duration between begin and end. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); +-------------------- + +/ + @property typeof(end - begin) length() const pure nothrow + { + return _end - _begin; + } + + + /++ + Whether the interval's length is 0, that is, whether begin == end. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); +-------------------- + +/ + @property bool empty() const pure nothrow + { + return _begin == _end; + } + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Throws: + DateTimeException if empty is true. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +-------------------- + +/ + bool contains(in TP timePoint) const pure + { + _enforceNotEmpty(); + + return timePoint >= _begin && timePoint < _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + DateTimeException if either interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + return interval._begin >= _begin && + interval._begin < _end && + interval._end <= _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false (unless this interval is empty), because an interval + going to positive infinity can never be contained in a finite interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool contains(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return false; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false (unless this interval is empty), because an interval + begging at negative infinity can never be contained in a finite interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool contains(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return false; + } + + + /++ + Whether this interval is before the given time point. + + Params: + timePoint = The time point to check whether this interval is before it. + + Throws: + DateTimeException if empty is true. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure + { + _enforceNotEmpty(); + + return _end <= timePoint; + } + + + /++ + Whether this interval is before the given interval and does not intersect + with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + DateTimeException if either interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); +-------------------- + +/ + bool isBefore(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not intersect + with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); +-------------------- + +/ + bool isBefore(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not intersect + with it. + + Always returns false (unless this interval is empty) because a finite + interval can never be before an interval beginning at negative infinity. + + Params: + interval = The interval to check for against this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool isBefore(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return false; + } + + + /++ + Whether this interval is after the given time point. + + Params: + timePoint = The time point to check whether this interval is after it. + + Throws: + DateTimeException if empty is true. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure + { + _enforceNotEmpty(); + + return timePoint < _begin; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Params: + interval = The interval to check against this interval. + + Throws: + DateTimeException if either interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool isAfter(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + return _begin >= interval._end; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Always returns false (unless this interval is empty) because a finite + interval can never be after an interval going to positive infinity. + + Params: + interval = The interval to check against this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool isAfter(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Params: + interval = The interval to check against this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); +-------------------- + +/ + bool isAfter(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return _begin >= interval._end; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + DateTimeException if either interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool intersects(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + return interval._begin < _end && interval._end > _begin; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool intersects(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return _end > interval._begin; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return _begin < interval._end; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect or if + either interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); +-------------------- + +/ + Interval intersection(in Interval interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto begin = _begin > interval._begin ? _begin : interval._begin; + auto end = _end < interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect or if this + interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval intersection(in PosInfInterval!TP interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval(_begin > interval._begin ? _begin : interval._begin, _end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect or if this + interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); +-------------------- + +/ + Interval intersection(in NegInfInterval!TP interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval(_begin, _end < interval._end ? _end : interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Throws: + DateTimeException if either interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + return _begin == interval._end || _end == interval._begin; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Throws: + DateTimeException if this is empty. + + Examples: +-------------------- +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return _end == interval._begin; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Throws: + DateTimeException if this is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); +assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return _begin == interval._end; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + DateTimeException if the two intervals do not intersect and are not + adjacent or if either interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); +-------------------- + +/ + Interval merge(in Interval interval) const + { + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + auto begin = _begin < interval._begin ? _begin : interval._begin; + auto end = _end > interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + DateTimeException if the two intervals do not intersect and are + not adjacent or if this interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval!TP merge(in PosInfInterval!TP interval) const + { + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + DateTimeException if the two intervals do not intersect and are not + adjacent or if this interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval!TP merge(in NegInfInterval!TP interval) const + { + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return NegInfInterval!TP(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + DateTimeException if either interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); +-------------------- + +/ + Interval span(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + auto begin = _begin < interval._begin ? _begin : interval._begin; + auto end = _end > interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval!TP span(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + DateTimeException if this interval is empty. + + Examples: +-------------------- +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == NegInfInterval!Date(Date(2012, 3 , 1))); +assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval!TP span(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + + return NegInfInterval!TP(_end > interval._end ? _end : interval._end); + } + + + /++ + Shifts the interval forward or backwards in time by the given duration + (a positive duration shifts the interval forward; a negative duration + shifts it backward). Effectively, it does $(D begin += duration) and + $(D end += duration). + + Params: + duration = The duration to shift the interval by. + + Throws: + DateTimeException if empty is true or if the resulting interval + would be invalid. + + Examples: +-------------------- +auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); +auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); +-------------------- + +/ + void shift(D)(D duration) pure + if(__traits(compiles, begin + duration)) + { + _enforceNotEmpty(); + + auto begin = _begin + duration; + auto end = _end + duration; + + if(!_valid(begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + + _begin = begin; + _end = end; + } + + + static if(__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Shifts the interval forward or backwards in time by the given number + of years and/or months (a positive number of years and months shifts + the interval forward; a negative number shifts it backward). + It adds the years the given years and months to both begin and end. + It effectively calls $(D add!"years"()) and then $(D add!"months"()) + on begin and end with the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on begin and end, causing their month to increment. + + Throws: + DateTimeException if empty is true or if the resulting interval + would be invalid. + + Examples: +-------------------- +auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); +auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + +interval1.shift(2); +assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + +interval2.shift(-2); +assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if(isIntegral!T) + { + _enforceNotEmpty(); + + auto begin = _begin; + auto end = _end; + + begin.add!"years"(years, allowOverflow); + begin.add!"months"(months, allowOverflow); + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + + _begin = begin; + _end = end; + } + } + + + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it does $(D begin -= duration) and/or $(D end += duration). Whether + it expands forwards and/or backwards in time is determined by the + $(D dir) parameter. + + Params: + duration = The duration to expand the interval by. + dir = The direction in time to expand the interval. + + Throws: + DateTimeException if empty is true or if the resulting interval would + be invalid. + + Examples: +-------------------- +auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); +auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + +interval1.expand(2); +assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + +interval2.expand(-2); +assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); +-------------------- + +/ + void expand(D)(D duration, Direction dir = Direction.both) pure + if(__traits(compiles, begin + duration)) + { + _enforceNotEmpty(); + + switch(dir) + { + case Direction.both: + { + auto begin = _begin - duration; + auto end = _end + duration; + + if(!_valid(begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + + _begin = begin; + _end = end; + + return; + } + case Direction.fwd: + { + auto end = _end + duration; + + if(!_valid(_begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + _end = end; + + return; + } + case Direction.bwd: + { + auto begin = _begin - duration; + + if(!_valid(begin, _end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + _begin = begin; + + return; + } + default: + assert(0, "Invalid Direction."); + } + } + + static if(__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it subtracts the given number of months/years from begin and adds + them to end. Whether it expands forwards and/or backwards in time + is determined by the dir parameter. + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on begin and end, causing their month to increment. + + Throws: + DateTimeException if empty is true or if the resulting interval + would be invalid. + + Examples: +-------------------- +auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); +auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + +interval1.expand(2); +assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + +interval2.expand(-2); +assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes, Direction dir = Direction.both) + if(isIntegral!T) + { + _enforceNotEmpty(); + + switch(dir) + { + case Direction.both: + { + auto begin = _begin; + auto end = _end; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + _begin = begin; + _end = end; + + return; + } + case Direction.fwd: + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(_begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + _end = end; + + return; + } + case Direction.bwd: + { + auto begin = _begin; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + + enforce(_valid(begin, _end), new DateTimeException("Argument would result in an invalid Interval.")); + _begin = begin; + + return; + } + default: + assert(0, "Invalid Direction."); + } + } + } + + + /++ + Returns a range which iterates forward over the interval, starting + at begin, using $(D func) to generate each successive time point. + + The range's $(D front) is the interval's $(D begin). $(D func) is used + to generate the next $(D front) when $(D popFront()) is called. If + $(D popFirst) is $(D PopFirst.yes), then $(D popFront()) is called + before the range is returned (so that front is a time point which + $(D func) would generate). + + If $(D func) ever generates a time point less than or equal to the + current front of the range, then a DateTimeException will be thrown. + The range will be empty and iteration complete when $(D func) generates + a time point equal to or beyond the end of the interval. + + There are helper functions in this module which generate common delegates + to pass to $(D fwdRange()). Their documentation starts with + "Generates a range-generating function for intervals," so you can easily + search for them. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront()) should be called on the range + before returning it. + + Throws: + DateTimeException if the interval is empty. + + Warning: + func must be logically pure. Ideally, $(D func) would be a function + pointer to a pure function, but forcing func to be pure is far too + restrictive to be useful, and in order to have the ease of use of + having functions which generate functions to pass to $(D fwdRange()), + $(D func) must be a delegate. + + If $(D func) retains state which changes as it is called, then some + algorithms will not work correctly, because the range's $(D save) + will have failed to have really saved the range's state. So, if you + want to avoid such bugs, don't pass a delegate which is not + logically pure to $(D fwdRange()). If $(D func) is given the same + time point with two different calls, it must return the same result + both times. + + Of course, none of the functions in this module have this problem, so + it's only relevant if you're creating your own delegate. + + Examples: +-------------------- +auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); +auto func = (in Date date) //For iterating over even-numbered days. + { + if((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; +auto range = interval.fwdRange(func); + +assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(range.empty); +-------------------- + +/ + IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + _enforceNotEmpty(); + + auto range = IntervalRange!(TP, Direction.fwd)(this, func); + + if(popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /++ + Returns a range which iterates backwards over the interval, starting + at end, using $(D func) to generate each successive time point. + + The range's $(D front) is the interval's $(D end). $(D func) is used + to generate the next $(D front) when $(D popFront()) is called. If + $(D popFirst) is $(D PopFirst.yes), then $(D popFront()) is called + before the range is returned (so that $(D front) is a time point which + $(D func) would generate). + + If $(D func) ever generates a time point greater than or equal to the + current $(D front) of the range, then a DateTimeException will be thrown. + The range will be empty and iteration complete when $(D func) generates + a time point equal to or less than the $(D begin) of the interval. + + There are helper functions in this module which generate common delegates + to pass to $(D bwdRange()). Their documentation starts with + "Generates a range-generating function for intervals," so you can easily + search for them. + + Params: + func = The function used to generate the time points of the range + over the interval. + popFirst = Whether $(D popFront()) should be called on the range before + returning it. + + Throws: + DateTimeException if the interval is empty. + + Warning: + func must be logically pure. Ideally, $(D func) would be a function + pointer to a pure function, but forcing func to be pure is far too + restrictive to be useful, and in order to have the ease of use of + having functions which generate functions to pass to $(D fwdRange()), + $(D func) must be a delegate. + + If $(D func) retains state which changes as it is called, then some + algorithms will not work correctly, because the range's $(D save) + will have failed to have really saved the range's state. So, if you + want to avoid such bugs, don't pass a delegate which is not + logically pure to $(D fwdRange()). If $(D func) is given the same + time point with two different calls, it must return the same result + both times. + + Of course, none of the functions in this module have this problem, so + it's only relevant if you're creating your own delegate. + + Examples: +-------------------- +auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); +auto func = (in Date date) //For iterating over even-numbered days. + { + if((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; +auto range = interval.bwdRange(func); + +assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(range.empty); +-------------------- + +/ + IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + _enforceNotEmpty(); + + auto range = IntervalRange!(TP, Direction.bwd)(this, func); + + if(popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + + +private: + + /++ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + try + return format("[%s - %s)", _begin, _end); + catch(Exception e) + assert(0, "format() threw."); + } + + + /++ + Throws: + DateTimeException if empty is true. + +/ + void _enforceNotEmpty(size_t line = __LINE__) const pure + { + if(empty) + throw new DateTimeException("Invalid operation for an empty Interval.", __FILE__, line); + } + + + /++ + Whether the given values form a valid time interval. + + Params: + begin = The begin point of the interval. + end = The end point of the interval. + +/ + static bool _valid(in TP begin, in TP end) pure nothrow + { + return begin <= end; + } + + + pure invariant() + { + assert(_valid(_begin, _end), "Invariant Failure: begin is not before or equal to end."); + } + + + TP _begin; + TP _end; +} + +//Test Interval's constructors. +unittest +{ + assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); + + Interval!Date(Date.init, Date.init); + Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); + Interval!DateTime(DateTime.init, DateTime.init); + Interval!SysTime(SysTime(0), SysTime(0)); + + Interval!DateTime(DateTime.init, dur!"days"(7)); + + //Verify Examples. + Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); +} + +//Test Interval's begin. +unittest +{ + assertPred!"=="(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin, Date(1, 1, 1)); + assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin, Date(2010, 1, 1)); + assertPred!"=="(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin, Date(1997, 12, 31)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.begin)); + static assert(__traits(compiles, iInterval.begin)); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); +} + +//Test Interval's end. +unittest +{ + assertPred!"=="(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end, Date(2010, 1, 1)); + assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end, Date(2010, 1, 1)); + assertPred!"=="(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end, Date(1998, 1, 1)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.end)); + static assert(__traits(compiles, iInterval.end)); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); +} + +//Test Interval's length. +unittest +{ + assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length, dur!"days"(0)); + assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length, dur!"days"(90)); + assertPred!"=="(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length, dur!"seconds"(42_727)); + assertPred!"=="(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length, dur!"seconds"(129_127)); + assertPred!"=="(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length, dur!"seconds"(129_127)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.length)); + static assert(__traits(compiles, iInterval.length)); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); +} + +//Test Interval's empty. +unittest +{ + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); + assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); + assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); + assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); + assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.empty)); + static assert(__traits(compiles, iInterval.empty)); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); +} + +//Test Interval's contains(time point). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); + + assert(!interval.contains(Date(2009, 7, 4))); + assert(!interval.contains(Date(2010, 7, 3))); + assert(interval.contains(Date(2010, 7, 4))); + assert(interval.contains(Date(2010, 7, 5))); + assert(interval.contains(Date(2011, 7, 1))); + assert(interval.contains(Date(2012, 1, 6))); + assert(!interval.contains(Date(2012, 1, 7))); + assert(!interval.contains(Date(2012, 1, 8))); + assert(!interval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, interval.contains(cdate))); + static assert(__traits(compiles, cInterval.contains(cdate))); + static assert(__traits(compiles, iInterval.contains(cdate))); + + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +} + +//Test Interval's contains(Interval). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.contains(interval)); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); + + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.contains(interval))); + static assert(__traits(compiles, interval.contains(cInterval))); + static assert(__traits(compiles, interval.contains(iInterval))); + static assert(__traits(compiles, interval.contains(posInfInterval))); + static assert(__traits(compiles, interval.contains(cPosInfInterval))); + static assert(__traits(compiles, interval.contains(iPosInfInterval))); + static assert(__traits(compiles, interval.contains(negInfInterval))); + static assert(__traits(compiles, interval.contains(cNegInfInterval))); + static assert(__traits(compiles, interval.contains(iNegInfInterval))); + static assert(__traits(compiles, cInterval.contains(interval))); + static assert(__traits(compiles, cInterval.contains(cInterval))); + static assert(__traits(compiles, cInterval.contains(iInterval))); + static assert(__traits(compiles, cInterval.contains(posInfInterval))); + static assert(__traits(compiles, cInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, cInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, cInterval.contains(negInfInterval))); + static assert(__traits(compiles, cInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, cInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, iInterval.contains(interval))); + static assert(__traits(compiles, iInterval.contains(cInterval))); + static assert(__traits(compiles, iInterval.contains(iInterval))); + static assert(__traits(compiles, iInterval.contains(posInfInterval))); + static assert(__traits(compiles, iInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, iInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, iInterval.contains(negInfInterval))); + static assert(__traits(compiles, iInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, iInterval.contains(iNegInfInterval))); + + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test Interval's isBefore(time point). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); + + assert(!interval.isBefore(Date(2009, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 4))); + assert(!interval.isBefore(Date(2010, 7, 5))); + assert(!interval.isBefore(Date(2011, 7, 1))); + assert(!interval.isBefore(Date(2012, 1, 6))); + assert(interval.isBefore(Date(2012, 1, 7))); + assert(interval.isBefore(Date(2012, 1, 8))); + assert(interval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isBefore(cdate))); + static assert(__traits(compiles, cInterval.isBefore(cdate))); + static assert(__traits(compiles, iInterval.isBefore(cdate))); + + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +} + +//Test Interval's isBefore(Interval). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isBefore(interval)); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); + + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isBefore(interval))); + static assert(__traits(compiles, interval.isBefore(cInterval))); + static assert(__traits(compiles, interval.isBefore(iInterval))); + static assert(__traits(compiles, interval.isBefore(posInfInterval))); + static assert(__traits(compiles, interval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, interval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, interval.isBefore(negInfInterval))); + static assert(__traits(compiles, interval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, interval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(interval))); + static assert(__traits(compiles, cInterval.isBefore(cInterval))); + static assert(__traits(compiles, cInterval.isBefore(iInterval))); + static assert(__traits(compiles, cInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(interval))); + static assert(__traits(compiles, iInterval.isBefore(cInterval))); + static assert(__traits(compiles, iInterval.isBefore(iInterval))); + static assert(__traits(compiles, iInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(iNegInfInterval))); + + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test Interval's isAfter(time point). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); + + assert(interval.isAfter(Date(2009, 7, 4))); + assert(interval.isAfter(Date(2010, 7, 3))); + assert(!interval.isAfter(Date(2010, 7, 4))); + assert(!interval.isAfter(Date(2010, 7, 5))); + assert(!interval.isAfter(Date(2011, 7, 1))); + assert(!interval.isAfter(Date(2012, 1, 6))); + assert(!interval.isAfter(Date(2012, 1, 7))); + assert(!interval.isAfter(Date(2012, 1, 8))); + assert(!interval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isAfter(cdate))); + static assert(__traits(compiles, cInterval.isAfter(cdate))); + static assert(__traits(compiles, iInterval.isAfter(cdate))); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +} + +//Test Interval's isAfter(Interval). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAfter(interval)); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); + + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isAfter(interval))); + static assert(__traits(compiles, interval.isAfter(cInterval))); + static assert(__traits(compiles, interval.isAfter(iInterval))); + static assert(__traits(compiles, interval.isAfter(posInfInterval))); + static assert(__traits(compiles, interval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, interval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, interval.isAfter(negInfInterval))); + static assert(__traits(compiles, interval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, interval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(interval))); + static assert(__traits(compiles, cInterval.isAfter(cInterval))); + static assert(__traits(compiles, cInterval.isAfter(iInterval))); + static assert(__traits(compiles, cInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(interval))); + static assert(__traits(compiles, iInterval.isAfter(cInterval))); + static assert(__traits(compiles, iInterval.isAfter(iInterval))); + static assert(__traits(compiles, iInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(iNegInfInterval))); + + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); +} + +//Test Interval's intersects(). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.intersects(interval)); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); + + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.intersects(interval))); + static assert(__traits(compiles, interval.intersects(cInterval))); + static assert(__traits(compiles, interval.intersects(iInterval))); + static assert(__traits(compiles, interval.intersects(posInfInterval))); + static assert(__traits(compiles, interval.intersects(cPosInfInterval))); + static assert(__traits(compiles, interval.intersects(iPosInfInterval))); + static assert(__traits(compiles, interval.intersects(negInfInterval))); + static assert(__traits(compiles, interval.intersects(cNegInfInterval))); + static assert(__traits(compiles, interval.intersects(iNegInfInterval))); + static assert(__traits(compiles, cInterval.intersects(interval))); + static assert(__traits(compiles, cInterval.intersects(cInterval))); + static assert(__traits(compiles, cInterval.intersects(iInterval))); + static assert(__traits(compiles, cInterval.intersects(posInfInterval))); + static assert(__traits(compiles, cInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, cInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, cInterval.intersects(negInfInterval))); + static assert(__traits(compiles, cInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, cInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, iInterval.intersects(interval))); + static assert(__traits(compiles, iInterval.intersects(cInterval))); + static assert(__traits(compiles, iInterval.intersects(iInterval))); + static assert(__traits(compiles, iInterval.intersects(posInfInterval))); + static assert(__traits(compiles, iInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, iInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, iInterval.intersects(negInfInterval))); + static assert(__traits(compiles, iInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, iInterval.intersects(iNegInfInterval))); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); +} + +//Test Interval's intersection(). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); + + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); + + assertPred!"=="(interval.intersection(interval), interval); + assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval), + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.intersection(interval))); + static assert(__traits(compiles, interval.intersection(cInterval))); + static assert(__traits(compiles, interval.intersection(iInterval))); + static assert(__traits(compiles, interval.intersection(posInfInterval))); + static assert(__traits(compiles, interval.intersection(cPosInfInterval))); + static assert(__traits(compiles, interval.intersection(iPosInfInterval))); + static assert(__traits(compiles, interval.intersection(negInfInterval))); + static assert(__traits(compiles, interval.intersection(cNegInfInterval))); + static assert(__traits(compiles, interval.intersection(iNegInfInterval))); + static assert(__traits(compiles, cInterval.intersection(interval))); + static assert(__traits(compiles, cInterval.intersection(cInterval))); + static assert(__traits(compiles, cInterval.intersection(iInterval))); + static assert(__traits(compiles, cInterval.intersection(posInfInterval))); + static assert(__traits(compiles, cInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, cInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, cInterval.intersection(negInfInterval))); + static assert(__traits(compiles, cInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, cInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, iInterval.intersection(interval))); + static assert(__traits(compiles, iInterval.intersection(cInterval))); + static assert(__traits(compiles, iInterval.intersection(iInterval))); + static assert(__traits(compiles, iInterval.intersection(posInfInterval))); + static assert(__traits(compiles, iInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, iInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, iInterval.intersection(negInfInterval))); + static assert(__traits(compiles, iInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, iInterval.intersection(iNegInfInterval))); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); +} + +//Test Interval's isAdjacent(). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.isAdjacent(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAdjacent(interval)); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); + + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isAdjacent(interval))); + static assert(__traits(compiles, interval.isAdjacent(cInterval))); + static assert(__traits(compiles, interval.isAdjacent(iInterval))); + static assert(__traits(compiles, interval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(interval))); + static assert(__traits(compiles, cInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(interval))); + static assert(__traits(compiles, iInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(iNegInfInterval))); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); +} + +//Test Interval's merge(). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(I)(in Interval!Date interval1, in I interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); + + assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); + + assertPred!"=="(interval.merge(interval), interval); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval), + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + + assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.merge(interval))); + static assert(__traits(compiles, interval.merge(cInterval))); + static assert(__traits(compiles, interval.merge(iInterval))); + static assert(__traits(compiles, interval.merge(posInfInterval))); + static assert(__traits(compiles, interval.merge(cPosInfInterval))); + static assert(__traits(compiles, interval.merge(iPosInfInterval))); + static assert(__traits(compiles, interval.merge(negInfInterval))); + static assert(__traits(compiles, interval.merge(cNegInfInterval))); + static assert(__traits(compiles, interval.merge(iNegInfInterval))); + static assert(__traits(compiles, cInterval.merge(interval))); + static assert(__traits(compiles, cInterval.merge(cInterval))); + static assert(__traits(compiles, cInterval.merge(iInterval))); + static assert(__traits(compiles, cInterval.merge(posInfInterval))); + static assert(__traits(compiles, cInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, cInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, cInterval.merge(negInfInterval))); + static assert(__traits(compiles, cInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, cInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, iInterval.merge(interval))); + static assert(__traits(compiles, iInterval.merge(cInterval))); + static assert(__traits(compiles, iInterval.merge(iInterval))); + static assert(__traits(compiles, iInterval.merge(posInfInterval))); + static assert(__traits(compiles, iInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, iInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, iInterval.merge(negInfInterval))); + static assert(__traits(compiles, iInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, iInterval.merge(iNegInfInterval))); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +} + +//Test Interval's span(). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertPred!"=="(interval.span(interval), interval); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval), + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval), + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assertPred!"=="(interval.span(PosInfInterval!Date(Date(2010, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(interval.span(PosInfInterval!Date(Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.span(PosInfInterval!Date(Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.span(PosInfInterval!Date(Date(2012, 1, 6))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.span(PosInfInterval!Date(Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(interval.span(PosInfInterval!Date(Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + + assertPred!"=="(interval.span(NegInfInterval!Date(Date(2010, 7, 3))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.span(NegInfInterval!Date(Date(2010, 7, 4))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.span(NegInfInterval!Date(Date(2010, 7, 5))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.span(NegInfInterval!Date(Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.span(NegInfInterval!Date(Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(interval.span(NegInfInterval!Date(Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.span(interval))); + static assert(__traits(compiles, interval.span(cInterval))); + static assert(__traits(compiles, interval.span(iInterval))); + static assert(__traits(compiles, interval.span(posInfInterval))); + static assert(__traits(compiles, interval.span(cPosInfInterval))); + static assert(__traits(compiles, interval.span(iPosInfInterval))); + static assert(__traits(compiles, interval.span(negInfInterval))); + static assert(__traits(compiles, interval.span(cNegInfInterval))); + static assert(__traits(compiles, interval.span(iNegInfInterval))); + static assert(__traits(compiles, cInterval.span(interval))); + static assert(__traits(compiles, cInterval.span(cInterval))); + static assert(__traits(compiles, cInterval.span(iInterval))); + static assert(__traits(compiles, cInterval.span(posInfInterval))); + static assert(__traits(compiles, cInterval.span(cPosInfInterval))); + static assert(__traits(compiles, cInterval.span(iPosInfInterval))); + static assert(__traits(compiles, cInterval.span(negInfInterval))); + static assert(__traits(compiles, cInterval.span(cNegInfInterval))); + static assert(__traits(compiles, cInterval.span(iNegInfInterval))); + static assert(__traits(compiles, iInterval.span(interval))); + static assert(__traits(compiles, iInterval.span(cInterval))); + static assert(__traits(compiles, iInterval.span(iInterval))); + static assert(__traits(compiles, iInterval.span(posInfInterval))); + static assert(__traits(compiles, iInterval.span(cPosInfInterval))); + static assert(__traits(compiles, iInterval.span(iPosInfInterval))); + static assert(__traits(compiles, iInterval.span(negInfInterval))); + static assert(__traits(compiles, iInterval.span(cNegInfInterval))); + static assert(__traits(compiles, iInterval.span(iNegInfInterval))); + + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +} + +//Test Interval's shift(duration). +unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, in Duration duration) + { + interval.shift(duration); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); +} + +//Test Interval's shift(int, int, AllowDayOverflow). +unittest +{ + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.shift(years, months); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(5))); + static assert(!__traits(compiles, iInterval.shift(5))); + + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); +} + +//Test Interval's expand(Duration). +unittest +{ + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(I)(I interval, in Duration duration) + { + interval.expand(duration); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); +} + +//Test Interval's expand(int, int, AllowDayOverflow, Direction) +unittest +{ + { + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.expand(years, months); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow, dir); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); + + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(5))); + static assert(!__traits(compiles, iInterval.expand(5))); + + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); +} + +//Test Interval's fwdRange. +unittest +{ + { + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + + static void testInterval1(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + } + + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + static void testInterval2(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval2(interval)); + + assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); + + assertPred!"=="(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front, + Date(2010, 9, 12)); + + assertPred!"=="(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front, + Date(2010, 9, 17)); + } + + //Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = (in Date date) + { + if((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.empty); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + static assert(__traits(compiles, iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); +} + +//Test Interval's bwdRange. +unittest +{ + { + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + + static void testInterval1(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + } + + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + static void testInterval2(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval2(interval)); + + assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); + assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); + + assertPred!"=="(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front, + Date(2010, 10, 1)); + + assertPred!"=="(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front, + Date(2010, 9, 24)); + } + + //Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = (in Date date) + { + if((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.empty); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + static assert(__traits(compiles, iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); +} + +//Test Interval's toString(). +unittest +{ + assertPred!"=="(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString(), "[2010-Jul-04 - 2012-Jan-07)"); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.toString())); + static assert(__traits(compiles, iInterval.toString())); +} + + +/++ + Represents an interval of time which has positive infinity as its end point. + + Any ranges which iterate over a $(D PosInfInterval) are infinite. So, the + main purpose of using $(D PosInfInterval) is to create an infinite range + which starts at a fixed point in time and goes to positive infinity. + +/ +struct PosInfInterval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + + Examples: +-------------------- +auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +-------------------- + +/ + this(in TP begin) pure nothrow + { + _begin = cast(TP)begin; + } + + + /++ + Params: + rhs = The PosInfInterval to assign to this one. + +/ + /+ref+/ PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow + { + _begin = cast(TP)rhs._begin; + + return this; + } + + + /++ + Params: + rhs = The PosInfInterval to assign to this one. + +/ + /+ref+/ PosInfInterval opAssign(PosInfInterval rhs) pure nothrow + { + _begin = cast(TP)rhs._begin; + + return this; + } + + + /++ + The begin point of the interval. It is included in the interval. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); +-------------------- + +/ + @property TP begin() const pure nothrow + { + return cast(TP)_begin; + } + + + /++ + The begin point of the interval. It is included in the interval. + + Params: + timePoint = The time point to set begin to. + +/ + @property void begin(TP timePoint) pure nothrow + { + _begin = timePoint; + } + + + /++ + Whether the interval's length is 0. Always returns false. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); +-------------------- + +/ + @property bool empty() const pure nothrow + { + return false; + } + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); +-------------------- + +/ + bool contains(TP timePoint) const pure nothrow + { + return timePoint >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return interval._begin >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); +-------------------- + +/ + bool contains(in PosInfInterval interval) const pure nothrow + { + return interval._begin >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false because an interval going to positive infinity + can never contain an interval beginning at negative infinity. + + Params: + interval = The interval to check for inclusion in this interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool contains(in NegInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given time point. + + Always returns false because an interval going to positive infinity + can never be before any time point. + + Params: + timePoint = The time point to check whether this interval is before it. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given interval and does not intersect + with it. + + Always returns false (unless the given interval is empty) because an + interval going to positive infinity can never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Throws: + DateTimeException if either Interval is empty. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +-------------------- + +/ + bool isBefore(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return false; + } + + + /++ + Whether this interval is before the given interval and does not intersect + with it. + + Always returns false because an interval going to positive infinity can + never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); +-------------------- + +/ + bool isBefore(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given interval and does not intersect + with it. + + Always returns false because an interval going to positive infinity can + never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool isBefore(in NegInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given time point. + + Params: + timePoint = The time point to check whether this Interval is after it. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure nothrow + { + return timePoint < _begin; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Params: + interval = The interval to check against this interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool isAfter(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return _begin >= interval._end; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Always returns false because an interval going to positive infinity can + never be after another interval going to positive infinity. + + Params: + interval = The interval to check against this interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool isAfter(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Params: + interval = The interval to check against this interval. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool isAfter(in NegInfInterval!TP interval) const pure nothrow + { + return _begin >= interval._end; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool intersects(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return interval._end > _begin; + } + + + /++ + Whether the given interval overlaps with this interval. + + Always returns true because two intervals going to positive infinity + always overlap. + + Params: + interval = The interval to check for intersection with this interval. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool intersects(in PosInfInterval interval) const pure nothrow + { + return true; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure nothrow + { + return _begin < interval._end; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect or if the + given interval is empty. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); +-------------------- + +/ + Interval!TP intersection(in Interval!TP interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto begin = _begin > interval._begin ? _begin : interval._begin; + + return Interval!TP(begin, interval._end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1996, 1 , 2))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1999, 1 , 12))); +-------------------- + +/ + PosInfInterval intersection(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); +-------------------- + +/ + Interval!TP intersection(in NegInfInterval!TP interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval!TP(_begin, interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +-------------------- + +/ + bool isAdjacent(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return _begin == interval._end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Always returns false because two intervals going to positive infinity + can never be adjacent to one another. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Examples: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow + { + return _begin == interval._end; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + DateTimeException if the two intervals do not intersect and are not + adjacent or if the given interval is empty. + + Note: + There is no overload for $(D merge()) which takes a NegInfInterval. This + is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval merge(in Interval!TP interval) const + { + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Note: + There is no overload for $(D merge()) which takes a NegInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval merge(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Throws: + DateTimeException if the given interval is empty. + + Note: + There is no overload for $(D span()) which takes a NegInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == PosInfInterval!Date(Date(500, 8, 9))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval span(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Note: + There is no overload for $(D span()) which takes a NegInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval span(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Shifts the begin of this interval forward or backwards in time by the + given duration (a positive duration shifts the interval forward; a + negative duration shifts it backward). Effectively, it does + $(D begin += duration). + + Params: + duration = The duration to shift the interval by. + + Examples: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +-------------------- + +/ + void shift(D)(D duration) pure nothrow + if(__traits(compiles, begin + duration)) + { + _begin += duration; + } + + + static if(__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Shifts the begin of this interval forward or backwards in time by + the given number of years and/or months (a positive number of years + and months shifts the interval forward; a negative number shifts it + backward). It adds the years the given years and months to begin. + It effectively calls $(D add!"years"()) and then $(D add!"months"()) + on begin the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on begin, causing its month to increment. + + Throws: + DateTimeException if empty is true or if the resulting interval + would be invalid. + + Examples: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if(isIntegral!T) + { + auto begin = _begin; + + begin.add!"years"(years, allowOverflow); + begin.add!"months"(months, allowOverflow); + + _begin = begin; + } + } + + + /++ + Expands the interval backwards in time. Effectively, it does + $(D begin -= duration). + + Params: + duration = The duration to expand the interval by. + dir = The direction in time to expand the interval. + + Examples: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.expand(dur!"days"(2)); +assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + +interval2.expand(dur!"days"(-2)); +assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); +-------------------- + +/ + void expand(D)(D duration) pure nothrow + if(__traits(compiles, begin + duration)) + { + _begin -= duration; + } + + + static if(__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it subtracts the given number of months/years from begin. + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on begin, causing its month to increment. + + Throws: + DateTimeException if empty is true or if the resulting interval + would be invalid. + + Examples: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.expand(2); +assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + +interval2.expand(-2); +assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if(isIntegral!T) + { + auto begin = _begin; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + + _begin = begin; + + return; + } + } + + + /++ + Returns a range which iterates forward over the interval, starting + at begin, using $(D func()) to generate each successive time point. + + The range's front is the interval's begin. $(D func()) is used + to generate the next front when $(D popFront()) is called. If + $(D popFirst) is true, then $(D popFront()) is called before the + range is returned (so that front is a time point which $(D func()) + would generate). + + If $(D func()) ever generates a time point less than or equal to the + current front of the range, then a DateTimeException will be thrown. + + There are helper functions in this module which generate common delegates + to pass to $(D fwdRange()). Their documentation starts with + "Generates a range-generating function for intervals," so you can easily + search for them. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether popFront() should be called on the range before + returning it. + + Warning: + func must be logically pure. Ideally, $(D func) would be a function + pointer to a pure function, but forcing func to be pure is far too + restrictive to be useful, and in order to have the ease of use of + having functions which generate functions to pass to $(D fwdRange()), + $(D func) must be a delegate. + + If $(D func) retains state which changes as it is called, then some + algorithms will not work correctly, because the range's $(D save) + will have failed to have really saved the range's state. So, if you + want to avoid such bugs, don't pass a delegate which is not + logically pure to $(D fwdRange()). If $(D func) is given the same + time point with two different calls, it must return the same result + both times. + + Of course, none of the functions in this module have this problem, so + it's only relevant if you're creating your own delegate. + + Examples: +-------------------- +auto interval = PosInfInterval!Date(Date(2010, 9, 1)); +auto func = (in Date date) //For iterating over even-numbered days. + { + if((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; +auto range = interval.fwdRange(func); + +assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(!range.empty); +-------------------- + +/ + PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + auto range = PosInfIntervalRange!(TP)(this, func); + + if(popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + +private: + + /++ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + try + return format("[%s - ∞)", _begin); + catch(Exception e) + assert(0, "format() threw."); + } + + + TP _begin; +} + +//Test PosInfInterval's constructor. +unittest +{ + PosInfInterval!Date(Date.init); + PosInfInterval!TimeOfDay(TimeOfDay.init); + PosInfInterval!DateTime(DateTime.init); + PosInfInterval!SysTime(SysTime(0)); + + //Verify Examples. + auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +} + +//Test PosInfInterval's begin. +unittest +{ + assertPred!"=="(PosInfInterval!Date(Date(1, 1, 1)).begin, Date(1, 1, 1)); + assertPred!"=="(PosInfInterval!Date(Date(2010, 1, 1)).begin, Date(2010, 1, 1)); + assertPred!"=="(PosInfInterval!Date(Date(1997, 12, 31)).begin, Date(1997, 12, 31)); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, cPosInfInterval.begin)); + static assert(__traits(compiles, iPosInfInterval.begin)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); +} + +//Test PosInfInterval's empty. +unittest +{ + assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + + const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cPosInfInterval.empty)); + static assert(__traits(compiles, iPosInfInterval.empty)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); +} + +//Test PosInfInterval's contains(time point). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.contains(Date(2009, 7, 4))); + assert(!posInfInterval.contains(Date(2010, 7, 3))); + assert(posInfInterval.contains(Date(2010, 7, 4))); + assert(posInfInterval.contains(Date(2010, 7, 5))); + assert(posInfInterval.contains(Date(2011, 7, 1))); + assert(posInfInterval.contains(Date(2012, 1, 6))); + assert(posInfInterval.contains(Date(2012, 1, 7))); + assert(posInfInterval.contains(Date(2012, 1, 8))); + assert(posInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, posInfInterval.contains(cdate))); + static assert(__traits(compiles, cPosInfInterval.contains(cdate))); + static assert(__traits(compiles, iPosInfInterval.contains(cdate))); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); +} + +//Test PosInfInterval's contains(Interval). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.contains(posInfInterval)); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); + + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.contains(interval))); + static assert(__traits(compiles, posInfInterval.contains(cInterval))); + static assert(__traits(compiles, posInfInterval.contains(iInterval))); + static assert(__traits(compiles, posInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(interval))); + static assert(__traits(compiles, cPosInfInterval.contains(cInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(iInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(interval))); + static assert(__traits(compiles, iPosInfInterval.contains(cInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(iInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(iNegInfInterval))); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test PosInfInterval's isBefore(time point). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.isBefore(Date(2009, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 4))); + assert(!posInfInterval.isBefore(Date(2010, 7, 5))); + assert(!posInfInterval.isBefore(Date(2011, 7, 1))); + assert(!posInfInterval.isBefore(Date(2012, 1, 6))); + assert(!posInfInterval.isBefore(Date(2012, 1, 7))); + assert(!posInfInterval.isBefore(Date(2012, 1, 8))); + assert(!posInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, posInfInterval.isBefore(cdate))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cdate))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cdate))); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); +} + +//Test PosInfInterval's isBefore(Interval). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isBefore(posInfInterval)); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); + + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.isBefore(interval))); + static assert(__traits(compiles, posInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(interval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(interval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(iNegInfInterval))); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test PosInfInterval's isAfter(time point). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(posInfInterval.isAfter(Date(2009, 7, 3))); + assert(posInfInterval.isAfter(Date(2010, 7, 3))); + assert(!posInfInterval.isAfter(Date(2010, 7, 4))); + assert(!posInfInterval.isAfter(Date(2010, 7, 5))); + assert(!posInfInterval.isAfter(Date(2011, 7, 1))); + assert(!posInfInterval.isAfter(Date(2012, 1, 6))); + assert(!posInfInterval.isAfter(Date(2012, 1, 7))); + assert(!posInfInterval.isAfter(Date(2012, 1, 8))); + assert(!posInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, posInfInterval.isAfter(cdate))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cdate))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cdate))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); +} + +//Test PosInfInterval's isAfter(Interval). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAfter(posInfInterval)); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); + + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.isAfter(interval))); + static assert(__traits(compiles, posInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(interval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(interval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(iNegInfInterval))); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's intersects(). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.intersects(posInfInterval)); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); + + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.intersects(interval))); + static assert(__traits(compiles, posInfInterval.intersects(cInterval))); + static assert(__traits(compiles, posInfInterval.intersects(iInterval))); + static assert(__traits(compiles, posInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(interval))); + static assert(__traits(compiles, cPosInfInterval.intersects(cInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(iInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(interval))); + static assert(__traits(compiles, iPosInfInterval.intersects(cInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(iInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's intersection(). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); + + assertPred!"=="(posInfInterval.intersection(posInfInterval), + posInfInterval); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); + assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), + Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); + + assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 5))); + assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))), + PosInfInterval!Date(Date(2012, 1, 6))); + assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))), + PosInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))), + PosInfInterval!Date(Date(2012, 1, 8))); + + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 5))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval), + PosInfInterval!Date(Date(2012, 1, 6))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval), + PosInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval), + PosInfInterval!Date(Date(2012, 1, 8))); + + assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.intersection(interval))); + static assert(__traits(compiles, posInfInterval.intersection(cInterval))); + static assert(__traits(compiles, posInfInterval.intersection(iInterval))); + static assert(__traits(compiles, posInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(interval))); + static assert(__traits(compiles, cPosInfInterval.intersection(cInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(iInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(interval))); + static assert(__traits(compiles, iPosInfInterval.intersection(cInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(iInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1996, 1 , 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1999, 1 , 12))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); +} + +//Test PosInfInterval's isAdjacent(). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAdjacent(posInfInterval)); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); + + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's merge(). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.merge(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + + assertPred!"=="(posInfInterval.merge(posInfInterval), + posInfInterval); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 1))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), + PosInfInterval!Date(Date(2010, 7, 4))); + + assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.merge(interval))); + static assert(__traits(compiles, posInfInterval.merge(cInterval))); + static assert(__traits(compiles, posInfInterval.merge(iInterval))); + static assert(__traits(compiles, posInfInterval.merge(posInfInterval))); + static assert(__traits(compiles, posInfInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.merge(iPosInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(interval))); + static assert(__traits(compiles, cPosInfInterval.merge(cInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(iInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(iPosInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(interval))); + static assert(__traits(compiles, iPosInfInterval.merge(cInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(iInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(iPosInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); +} + +//Test PosInfInterval's span(). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.span(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertPred!"=="(posInfInterval.span(posInfInterval), + posInfInterval); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 1))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 1))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), + PosInfInterval!Date(Date(2010, 7, 4))); + + assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))), + PosInfInterval!Date(Date(2010, 7, 4))); + + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval), + PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.span(interval))); + static assert(__traits(compiles, posInfInterval.span(cInterval))); + static assert(__traits(compiles, posInfInterval.span(iInterval))); + static assert(__traits(compiles, posInfInterval.span(posInfInterval))); + static assert(__traits(compiles, posInfInterval.span(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.span(iPosInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.span(interval))); + static assert(__traits(compiles, cPosInfInterval.span(cInterval))); + static assert(__traits(compiles, cPosInfInterval.span(iInterval))); + static assert(__traits(compiles, cPosInfInterval.span(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.span(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.span(iPosInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.span(interval))); + static assert(__traits(compiles, iPosInfInterval.span(cInterval))); + static assert(__traits(compiles, iPosInfInterval.span(iInterval))); + static assert(__traits(compiles, iPosInfInterval.span(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.span(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.span(iPosInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == PosInfInterval!Date(Date(500, 8, 9))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); +} + +//Test PosInfInterval's shift(). +unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); + + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +} + +//Test PosInfInterval's shift(int, int, AllowDayOverflow). +unittest +{ + { + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + } + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.shift(1))); + static assert(!__traits(compiles, iPosInfInterval.shift(1))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.shift(2); + assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); + + interval2.shift(-2); + assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); +} + +//Test PosInfInterval's expand(). +unittest +{ + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); + + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); +} + +//Test PosInfInterval's expand(int, int, AllowDayOverflow). +unittest +{ + { + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + } + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.expand(1))); + static assert(!__traits(compiles, iPosInfInterval.expand(1))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.expand(2); + assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + + interval2.expand(-2); + assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); +} + +//Test PosInfInterval's fwdRange(). +unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); + + static void testInterval(PosInfInterval!Date posInfInterval) + { + posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval(posInfInterval)); + + assertPred!"=="(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front, + Date(2010, 9, 12)); + + assertPred!"=="(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front, + Date(2010, 9, 17)); + + //Verify Examples. + auto interval = PosInfInterval!Date(Date(2010, 9, 1)); + auto func = (in Date date) + { + if((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(!range.empty); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + static assert(__traits(compiles, iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); +} + +//Test PosInfInterval's toString(). +unittest +{ + assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).toString(), "[2010-Jul-04 - ∞)"); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, cPosInfInterval.toString())); + static assert(__traits(compiles, iPosInfInterval.toString())); +} + + +/++ + Represents an interval of time which has negative infinity as its begin point. + + Any ranges which iterate over a $(D NegInfInterval) are infinite. So, the + main purpose of using $(D NegInfInterval) is to create an infinite range + which starts at negative infinity and goes to a fixed end point. You would + then iterate over it in reverse. + +/ +struct NegInfInterval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + + Examples: +-------------------- +auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +-------------------- + +/ + this(in TP end) pure nothrow + { + _end = cast(TP)end; + } + + + /++ + Params: + rhs = The NegInfInterval to assign to this one. + +/ + /+ref+/ NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow + { + _end = cast(TP)rhs._end; + + return this; + } + + + /++ + Params: + rhs = The NegInfInterval to assign to this one. + +/ + /+ref+/ NegInfInterval opAssign(NegInfInterval rhs) pure nothrow + { + _end = cast(TP)rhs._end; + + return this; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); +-------------------- + +/ + @property TP end() const pure nothrow + { + return cast(TP)_end; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Params: + timePoint = The time point to set end to. + +/ + @property void end(TP timePoint) pure nothrow + { + _end = timePoint; + } + + + /++ + Whether the interval's length is 0. Always returns false. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); +-------------------- + +/ + @property bool empty() const pure nothrow + { + return false; + } + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +-------------------- + +/ + bool contains(TP timePoint) const pure nothrow + { + return timePoint < _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return interval._end <= _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false because an interval beginning at negative infinity + can never contain an interval going to positive infinity. + + Params: + interval = The interval to check for inclusion in this interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool contains(in PosInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool contains(in NegInfInterval interval) const pure nothrow + { + return interval._end <= _end; + } + + + /++ + Whether this interval is before the given time point. + + Params: + timePoint = The time point to check whether this interval is + before it. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure nothrow + { + return timePoint >= _end; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + DateTimeException if the given interval is empty + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isBefore(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Params: + interval = The interval to check for against this interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isBefore(in PosInfInterval!TP interval) const pure nothrow + { + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Always returns false because an interval beginning at negative + infinity can never be before another interval beginning at negative + infinity. + + Params: + interval = The interval to check for against this interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool isBefore(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given time point. + + Always returns false because an interval begginning at negative infinity + can never be after any time point. + + Params: + timePoint = The time point to check whether this Interval is after it. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not + intersect with it. + + Always returns false (unless the given interval is empty) because an + interval beginning at negative infinity can never be after any other + interval. + + Params: + interval = The interval to check against this interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isAfter(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Always returns false because an interval beginning at negative infinity + can never be after any other interval. + + Params: + interval = The interval to check against this interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAfter(in PosInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + with it. + + Always returns false because an interval beginning at negative infinity + can never be after any other interval. + + Params: + interval = The interval to check against this interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool isAfter(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool intersects(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return interval._begin < _end; + } + + + /++ + Whether the given interval overlaps with this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool intersects(in PosInfInterval!TP interval) const pure nothrow + { + return interval._begin < _end; + } + + + /++ + Whether the given interval overlaps with this interval. + + Always returns true because two intervals beginning at negative infinity + always overlap. + + Params: + interval = The interval to check for intersection with this interval. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure nothrow + { + return true; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect or if the + given interval is empty. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval!TP intersection(in Interval!TP interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto end = _end < interval._end ? _end : interval._end; + + return Interval!TP(interval._begin, end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + DateTimeException if the two intervals do not intersect. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval!TP intersection(in PosInfInterval!TP interval) const + { + enforce(this.intersects(interval), new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval!TP(interval._begin, _end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(1999, 7 , 6))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2012, 3 , 1))); +-------------------- + +/ + NegInfInterval intersection(in NegInfInterval interval) const nothrow + { + return NegInfInterval(_end < interval._end ? _end : interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Throws: + DateTimeException if the given interval is empty. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isAdjacent(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return interval._begin == _end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow + { + return interval._begin == _end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Always returns false because two intervals beginning at negative + infinity can never be adjacent to one another. + + Params: + interval = The interval to check whether its adjacent with this + interval. + + Examples: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + DateTimeException if the two intervals do not intersect and are + not adjacent or if the given interval is empty. + + Note: + There is no overload for $(D merge()) which takes a PosInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); +-------------------- + +/ + NegInfInterval merge(in Interval!TP interval) const + { + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Note: + There is no overload for $(D merge()) which takes a PosInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval merge(in NegInfInterval interval) const pure nothrow + { + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Throws: + DateTimeException if the given interval is empty. + + Note: + There is no overload for $(D span()) which takes a PosInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); +assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == NegInfInterval!Date(Date(2017, 7 , 1))); +-------------------- + +/ + NegInfInterval span(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Note: + There is no overload for $(D span()) which takes a PosInfInterval. + This is because you can't have an interval which goes from negative + infinity to positive infinity. + + Examples: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval span(in NegInfInterval interval) const pure nothrow + { + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Shifts the end of this interval forward or backwards in time by the + given duration (a positive duration shifts the interval forward; a + negative duration shifts it backward). Effectively, it does + $(D end += duration). + + Params: + duration = The duration to shift the interval by. + + Examples: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); +auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); +-------------------- + +/ + void shift(D)(D duration) pure nothrow + if(__traits(compiles, end + duration)) + { + _end += duration; + } + + + static if(__traits(compiles, end.add!"months"(1)) && + __traits(compiles, end.add!"years"(1))) + { + /++ + Shifts the end of this interval forward or backwards in time by the + given number of years and/or months (a positive number of years and + months shifts the interval forward; a negative number shifts it + backward). It adds the years the given years and months to end. It + effectively calls $(D add!"years"()) and then $(D add!"months"()) + on end with the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow on end, causing its month to increment. + + Throws: + DateTimeException if empty is true or if the resulting interval would be invalid. + + Examples: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.shift(2); +assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + +interval2.shift(-2); +assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if(isIntegral!T) + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + _end = end; + } + } + + + /++ + Expands the interval forwards in time. Effectively, it does + $(D end += duration). + + Params: + duration = The duration to expand the interval by. + dir = The direction in time to expand the interval. + + Examples: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.expand(dur!"days"(2)); +assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + +interval2.expand(dur!"days"(-2)); +assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); +-------------------- + +/ + void expand(D)(D duration) pure nothrow + if(__traits(compiles, end + duration)) + { + _end += duration; + } + + + static if(__traits(compiles, end.add!"months"(1)) && + __traits(compiles, end.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it adds the given number of months/years to end. + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on end, causing their month to increment. + + Throws: + DateTimeException if empty is true or if the resulting interval + would be invalid. + + Examples: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.expand(2); +assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + +interval2.expand(-2); +assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if(isIntegral!T) + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + _end = end; + + return; + } + } + + + /++ + Returns a range which iterates backwards over the interval, starting + at end, using $(D func()) to generate each successive time point. + + The range's front is the interval's end. $(D func) is used to generate + the next front when $(D popFront()) is called. If $(D popFirst) + is true, then $(D popFront()) is called before the range is returned + (so that front is a time point which $(D func()) would generate). + + If $(D func()) ever generates a time point less than or equal to the + current front of the range, then a DateTimeException will be thrown. + + There are helper functions in this module which generate common delegates + to pass to $(D bwdRange()). Their documentation starts with + "Generates a range-generating function for intervals," so you can easily + search for them. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether popFront() should be called on the range before + returning it. + + Warning: + func must be logically pure. Ideally, $(D func) would be a function + pointer to a pure function, but forcing func to be pure is far too + restrictive to be useful, and in order to have the ease of use of + having functions which generate functions to pass to $(D fwdRange()), + $(D func) must be a delegate. + + If $(D func) retains state which changes as it is called, then some + algorithms will not work correctly, because the range's $(D save) + will have failed to have really saved the range's state. So, if you + want to avoid such bugs, don't pass a delegate which is not + logically pure to $(D fwdRange()). If $(D func) is given the same + time point with two different calls, it must return the same result + both times. + + Of course, none of the functions in this module have this problem, so + it's only relevant if you're creating your own delegate. + + Examples: +-------------------- +auto interval = NegInfInterval!Date(Date(2010, 9, 9)); +auto func = (in Date date) //For iterating over even-numbered days. + { + if((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; +auto range = interval.bwdRange(func); + +assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(!range.empty); +-------------------- + +/ + NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + auto range = NegInfIntervalRange!(TP)(this, func); + + if(popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + +private: + + /++ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + try + return format("[-∞ - %s)", _end); + catch(Exception e) + assert(0, "format() threw."); + } + + + TP _end; +} + +//Test NegInfInterval's constructor. +unittest +{ + NegInfInterval!Date(Date.init); + NegInfInterval!TimeOfDay(TimeOfDay.init); + NegInfInterval!DateTime(DateTime.init); + NegInfInterval!SysTime(SysTime(0)); +} + +//Test NegInfInterval's end. +unittest +{ + assertPred!"=="(NegInfInterval!Date(Date(2010, 1, 1)).end, Date(2010, 1, 1)); + assertPred!"=="(NegInfInterval!Date(Date(2010, 1, 1)).end, Date(2010, 1, 1)); + assertPred!"=="(NegInfInterval!Date(Date(1998, 1, 1)).end, Date(1998, 1, 1)); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.end)); + static assert(__traits(compiles, iNegInfInterval.end)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); +} + +//Test NegInfInterval's empty. +unittest +{ + assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.empty)); + static assert(__traits(compiles, iNegInfInterval.empty)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); +} + +//Test NegInfInterval's contains(time point). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(negInfInterval.contains(Date(2009, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 3))); + assert(negInfInterval.contains(Date(2010, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 5))); + assert(negInfInterval.contains(Date(2011, 7, 1))); + assert(negInfInterval.contains(Date(2012, 1, 6))); + assert(!negInfInterval.contains(Date(2012, 1, 7))); + assert(!negInfInterval.contains(Date(2012, 1, 8))); + assert(!negInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.contains(cdate))); + static assert(__traits(compiles, cNegInfInterval.contains(cdate))); + static assert(__traits(compiles, iNegInfInterval.contains(cdate))); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +} + +//Test NegInfInterval's contains(Interval). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.contains(negInfInterval)); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); + + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.contains(interval))); + static assert(__traits(compiles, negInfInterval.contains(cInterval))); + static assert(__traits(compiles, negInfInterval.contains(iInterval))); + static assert(__traits(compiles, negInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(interval))); + static assert(__traits(compiles, cNegInfInterval.contains(cInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(iInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(interval))); + static assert(__traits(compiles, iNegInfInterval.contains(cInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(iInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(iNegInfInterval))); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's isBefore(time point). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isBefore(Date(2009, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 3))); + assert(!negInfInterval.isBefore(Date(2010, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 5))); + assert(!negInfInterval.isBefore(Date(2011, 7, 1))); + assert(!negInfInterval.isBefore(Date(2012, 1, 6))); + assert(negInfInterval.isBefore(Date(2012, 1, 7))); + assert(negInfInterval.isBefore(Date(2012, 1, 8))); + assert(negInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isBefore(cdate))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cdate))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cdate))); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +} + +//Test NegInfInterval's isBefore(Interval). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isBefore(negInfInterval)); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); + + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isBefore(interval))); + static assert(__traits(compiles, negInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(interval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(interval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(iNegInfInterval))); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's isAfter(time point). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isAfter(Date(2009, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 3))); + assert(!negInfInterval.isAfter(Date(2010, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 5))); + assert(!negInfInterval.isAfter(Date(2011, 7, 1))); + assert(!negInfInterval.isAfter(Date(2012, 1, 6))); + assert(!negInfInterval.isAfter(Date(2012, 1, 7))); + assert(!negInfInterval.isAfter(Date(2012, 1, 8))); + assert(!negInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isAfter(cdate))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cdate))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cdate))); +} + +//Test NegInfInterval's isAfter(Interval). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAfter(negInfInterval)); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); + + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isAfter(interval))); + static assert(__traits(compiles, negInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(interval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(interval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(iNegInfInterval))); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's intersects(). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.intersects(negInfInterval)); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); + + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.intersects(interval))); + static assert(__traits(compiles, negInfInterval.intersects(cInterval))); + static assert(__traits(compiles, negInfInterval.intersects(iInterval))); + static assert(__traits(compiles, negInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(interval))); + static assert(__traits(compiles, cNegInfInterval.intersects(cInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(iInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(interval))); + static assert(__traits(compiles, iNegInfInterval.intersects(cInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(iInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(iNegInfInterval))); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's intersection(). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assertPred!"=="(negInfInterval.intersection(negInfInterval), + negInfInterval); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), + Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))), + NegInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))), + NegInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))), + NegInfInterval!Date(Date(2010, 7, 5))); + assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 6))); + assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 7))); + + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval), + NegInfInterval!Date(Date(2010, 7, 3))); + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval), + NegInfInterval!Date(Date(2010, 7, 4))); + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval), + NegInfInterval!Date(Date(2010, 7, 5))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 6))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + + assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))), + Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); + assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))), + Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); + assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))), + Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); + assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))), + Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.intersection(interval))); + static assert(__traits(compiles, negInfInterval.intersection(cInterval))); + static assert(__traits(compiles, negInfInterval.intersection(iInterval))); + static assert(__traits(compiles, negInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(interval))); + static assert(__traits(compiles, cNegInfInterval.intersection(cInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(iInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(interval))); + static assert(__traits(compiles, iNegInfInterval.intersection(cInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(iInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(iNegInfInterval))); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(1999, 7 , 6))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2012, 3 , 1))); +} + +//Test NegInfInterval's isAdjacent(). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAdjacent(negInfInterval)); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); + + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(iNegInfInterval))); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); +} + +//Test NegInfInterval's merge(). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertPred!"=="(negInfInterval.merge(negInfInterval), + negInfInterval); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + NegInfInterval!Date(Date(2013, 7, 3))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + + assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.merge(interval))); + static assert(__traits(compiles, negInfInterval.merge(cInterval))); + static assert(__traits(compiles, negInfInterval.merge(iInterval))); + static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.merge(negInfInterval))); + static assert(__traits(compiles, negInfInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(interval))); + static assert(__traits(compiles, cNegInfInterval.merge(cInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(iInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(interval))); + static assert(__traits(compiles, iNegInfInterval.merge(cInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(iInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(iNegInfInterval))); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +} + +//Test NegInfInterval's span(). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertPred!"=="(negInfInterval.span(negInfInterval), + negInfInterval); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), + NegInfInterval!Date(Date(2013, 7, 3))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), + NegInfInterval!Date(Date(2012, 1, 9))); + + assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))), + NegInfInterval!Date(Date(2012, 1, 8))); + + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 7))); + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval), + NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.span(interval))); + static assert(__traits(compiles, negInfInterval.span(cInterval))); + static assert(__traits(compiles, negInfInterval.span(iInterval))); + static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.span(negInfInterval))); + static assert(__traits(compiles, negInfInterval.span(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(interval))); + static assert(__traits(compiles, cNegInfInterval.span(cInterval))); + static assert(__traits(compiles, cNegInfInterval.span(iInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(interval))); + static assert(__traits(compiles, iNegInfInterval.span(cInterval))); + static assert(__traits(compiles, iNegInfInterval.span(iInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(iNegInfInterval))); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); + assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == NegInfInterval!Date(Date(2017, 7 , 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); +} + +//Test NegInfInterval's shift(). +unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); + auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); +} + +//Test NegInfInterval's shift(int, int, AllowDayOverflow). +unittest +{ + { + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testIntervalFail(I)(I interval, int years, int months) + { + interval.shift(years, months); + } + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); + } + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.shift(1))); + static assert(!__traits(compiles, iNegInfInterval.shift(1))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +} + +//Test NegInfInterval's expand(). +unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); +} + +//Test NegInfInterval's expand(int, int, AllowDayOverflow). +unittest +{ + { + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assertPred!"=="(interval, expected, "", __FILE__, line); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); + } + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.expand(1))); + static assert(!__traits(compiles, iNegInfInterval.expand(1))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +} + +//Test NegInfInterval's bwdRange(). +unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(NegInfInterval!Date negInfInterval) + { + negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval(negInfInterval)); + + assertPred!"=="(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front, + Date(2010, 10, 1)); + + assertPred!"=="(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front, + Date(2010, 9, 24)); + + //Verify Examples. + auto interval = NegInfInterval!Date(Date(2010, 9, 9)); + auto func = (in Date date) + { + if((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(!range.empty); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); + static assert(__traits(compiles, iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); +} + +//Test NegInfInterval's toString(). +unittest +{ + assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).toString(), "[-∞ - 2012-Jan-07)"); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.toString())); + static assert(__traits(compiles, iNegInfInterval.toString())); +} + + +/++ + Generates a range-generating function for intervals. + + Returns a delegate which returns the next time point with the given + DayOfWeek in a range. + + Using this delegate allows you to iterate over successive time points + which are all the same day of the week. e.g. passing DayOfWeek.mon + to $(D everyDayOfWeek()) would result in a delegate which could be used + to iterate over all of the Mondays in a range. + + Params: + dir = The direction to iterate in. If passing the return value + to $(D fwdRange()), use $(D Direction.fwd). If passing + it to $(D bwdRange()), use $(D Direction.bwd). + dayOfWeek = The week that each time point in the range will be. + + Examples: +-------------------- +auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); +auto func = everyDayOfWeek!Date(DayOfWeek.mon); +auto range = interval.fwdRange(func); + +assert(range.front == Date(2010, 9, 2)); //A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 13)); + +range.popFront(); +assert(range.front == Date(2010, 9, 20)); + +range.popFront(); +assert(range.empty); +-------------------- + +/ +static TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow + if(isTimePoint!TP && + (dir == Direction.fwd || dir == Direction.bwd) && + __traits(hasMember, TP, "dayOfWeek") && + !__traits(isStaticFunction, TP.dayOfWeek) && + is(ReturnType!(TP.dayOfWeek) == DayOfWeek) && + (functionAttributes!(TP.dayOfWeek) & FunctionAttribute.PROPERTY) && + (functionAttributes!(TP.dayOfWeek) & FunctionAttribute.NOTHROW)) +{ + TP func(in TP tp) + { + TP retval = cast(TP)tp; + immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); + + static if(dir == Direction.fwd) + immutable adjustedDays = days == 0 ? 7 : days; + else + immutable adjustedDays = days == 0 ? -7 : days - 7; + + return retval += dur!"days"(adjustedDays); + } + + return &func; +} + +unittest +{ + auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); + auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); + + assertPred!"=="(funcFwd(Date(2010, 8, 28)), Date(2010, 8, 30)); + assertPred!"=="(funcFwd(Date(2010, 8, 29)), Date(2010, 8, 30)); + assertPred!"=="(funcFwd(Date(2010, 8, 30)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 8, 31)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 9, 1)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 9, 2)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 9, 3)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 9, 4)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 9, 5)), Date(2010, 9, 6)); + assertPred!"=="(funcFwd(Date(2010, 9, 6)), Date(2010, 9, 13)); + assertPred!"=="(funcFwd(Date(2010, 9, 7)), Date(2010, 9, 13)); + + assertPred!"=="(funcBwd(Date(2010, 8, 28)), Date(2010, 8, 23)); + assertPred!"=="(funcBwd(Date(2010, 8, 29)), Date(2010, 8, 23)); + assertPred!"=="(funcBwd(Date(2010, 8, 30)), Date(2010, 8, 23)); + assertPred!"=="(funcBwd(Date(2010, 8, 31)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 1)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 2)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 3)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 4)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 5)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 6)), Date(2010, 8, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 7)), Date(2010, 9, 6)); + + static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); + static assert(__traits(compiles, everyDayOfWeek!(DateTime)(DayOfWeek.mon))); + static assert(__traits(compiles, everyDayOfWeek!(SysTime)(DayOfWeek.mon))); + + //Verify Examples. + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDayOfWeek!Date(DayOfWeek.mon); + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 2)); //A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 13)); + + range.popFront(); + assert(range.front == Date(2010, 9, 20)); + + range.popFront(); + assert(range.empty); +} + + +/++ + Generates a range-generating function for intervals. + + Returns a delegate which returns the next time point with the given + month which would be reached by adding months to the given time + point. + + So, using this delegate allows you to iterate over successive time + points which are in the same month but different years. For example, + you could iterate over each successive December 25th in an interval + by starting with a date which had the 25th as its day and passed + $(D Month.dec) to $(D everyMonth()) to create the delegate. + + Since it wouldn't really make sense to be iterating over a specific + month and end up with some of the time points in the succeeding month + or two years after the previous time point, $(D AllowDayOverflow.no) + is always used when calculating the next time point. + + Params: + dir = The direction to iterate in. If passing the return value + to $(D fwdRange()), use $(D Direction.fwd). If passing + it to $(D bwdRange()), use $(D Direction.bwd). + month = The month that each time point in the range will be in. + + Examples: +-------------------- +auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); +auto func = everyMonth!(Date)(Month.feb); +auto range = interval.fwdRange(func); + +assert(range.front == Date(2000, 1, 30)); //Using PopFirst.yes would have made this Date(2010, 2, 29). + +range.popFront(); +assert(range.front == Date(2000, 2, 29)); + +range.popFront(); +assert(range.front == Date(2001, 2, 28)); + +range.popFront(); +assert(range.front == Date(2002, 2, 28)); + +range.popFront(); +assert(range.front == Date(2003, 2, 28)); + +range.popFront(); +assert(range.front == Date(2004, 2, 28)); + +range.popFront(); +assert(range.empty); +-------------------- + +/ +static TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) + if(isTimePoint!TP && + (dir == Direction.fwd || dir == Direction.bwd) && + __traits(hasMember, TP, "month") && + !__traits(isStaticFunction, TP.month) && + is(ReturnType!(TP.month) == Month) && + (functionAttributes!(TP.month) & FunctionAttribute.PROPERTY) && + (functionAttributes!(TP.month) & FunctionAttribute.NOTHROW)) +{ + enforceValid!"months"(month); + + TP func(in TP tp) + { + TP retval = cast(TP)tp; + immutable months = monthsToMonth(retval.month, month); + + static if(dir == Direction.fwd) + immutable adjustedMonths = months == 0 ? 12 : months; + else + immutable adjustedMonths = months == 0 ? -12 : months - 12; + + retval.add!"months"(adjustedMonths, AllowDayOverflow.no); + + if(retval.month != month) + { + retval.add!"months"(-1); + assert(retval.month == month); + } + + return retval; + } + + return &func; +} + +unittest +{ + auto funcFwd = everyMonth!Date(Month.jun); + auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); + + assertPred!"=="(funcFwd(Date(2010, 5, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 6, 30)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 7, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 8, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 9, 30)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 10, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 11, 30)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2010, 12, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2011, 1, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2011, 2, 28)), Date(2011, 6, 28)); + assertPred!"=="(funcFwd(Date(2011, 3, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2011, 4, 30)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2011, 5, 31)), Date(2011, 6, 30)); + assertPred!"=="(funcFwd(Date(2011, 6, 30)), Date(2012, 6, 30)); + assertPred!"=="(funcFwd(Date(2011, 7, 31)), Date(2012, 6, 30)); + + assertPred!"=="(funcBwd(Date(2010, 5, 31)), Date(2009, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 6, 30)), Date(2009, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 7, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 8, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 9, 30)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 10, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 11, 30)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2010, 12, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2011, 1, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2011, 2, 28)), Date(2010, 6, 28)); + assertPred!"=="(funcBwd(Date(2011, 3, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2011, 4, 30)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2011, 5, 31)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2011, 6, 30)), Date(2010, 6, 30)); + assertPred!"=="(funcBwd(Date(2011, 7, 30)), Date(2011, 6, 30)); + + static assert(!__traits(compiles, everyMonth!(TimeOfDay)(Month.jan))); + static assert(__traits(compiles, everyMonth!(DateTime)(Month.jan))); + static assert(__traits(compiles, everyMonth!(SysTime)(Month.jan))); + + //Verify Examples. + auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); + auto func = everyMonth!(Date)(Month.feb); + auto range = interval.fwdRange(func); + + assert(range.front == Date(2000, 1, 30)); //Using PopFirst.yes would have made this Date(2010, 2, 29). + + range.popFront(); + assert(range.front == Date(2000, 2, 29)); + + range.popFront(); + assert(range.front == Date(2001, 2, 28)); + + range.popFront(); + assert(range.front == Date(2002, 2, 28)); + + range.popFront(); + assert(range.front == Date(2003, 2, 28)); + + range.popFront(); + assert(range.front == Date(2004, 2, 28)); + + range.popFront(); + assert(range.empty); +} + + +/++ + Generates a range-generating function for intervals. + + Returns a delegate which returns the next time point which is the given + duration later. + + Using this delegate allows you to iterate over successive time points + which are apart by the given duration e.g. passing $(D dur!"days"(3)) to + $(D everyDuration()) would result in a delegate which could be + used to iterate over a range of days which are each 3 days apart. + + Params: + dir = The direction to iterate in. If passing the return value + to $(D fwdRange()), use $(D Direction.fwd). If passing it + to $(D bwdRange()), use $(D Direction.bwd). + duration = The duration which separates each successive time point + in the range. + + Examples: +-------------------- +auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); +auto func = everyDuration!Date(dur!"days"(8)); +auto range = interval.fwdRange(func); + +assert(range.front == Date(2010, 9, 2)); //Using PopFirst.yes would have made this Date(2010, 9, 10). + +range.popFront(); +assert(range.front == Date(2010, 9, 10)); + +range.popFront(); +assert(range.front == Date(2010, 9, 18)); + +range.popFront(); +assert(range.front == Date(2010, 9, 26)); + +range.popFront(); +assert(range.empty); +-------------------- + +/ +static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) + (D duration) nothrow + if(isTimePoint!TP && + __traits(compiles, TP.init + duration) && + (dir == Direction.fwd || dir == Direction.bwd)) +{ + TP func(in TP tp) + { + static if(dir == Direction.fwd) + return tp + duration; + else + return tp - duration; + } + + return &func; +} + +unittest +{ + auto funcFwd = everyDuration!Date(dur!"days"(27)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); + + assertPred!"=="(funcFwd(Date(2009, 12, 25)), Date(2010, 1, 21)); + assertPred!"=="(funcFwd(Date(2009, 12, 26)), Date(2010, 1, 22)); + assertPred!"=="(funcFwd(Date(2009, 12, 27)), Date(2010, 1, 23)); + assertPred!"=="(funcFwd(Date(2009, 12, 28)), Date(2010, 1, 24)); + + assertPred!"=="(funcBwd(Date(2010, 1, 21)), Date(2009, 12, 25)); + assertPred!"=="(funcBwd(Date(2010, 1, 22)), Date(2009, 12, 26)); + assertPred!"=="(funcBwd(Date(2010, 1, 23)), Date(2009, 12, 27)); + assertPred!"=="(funcBwd(Date(2010, 1, 24)), Date(2009, 12, 28)); + + static assert(__traits(compiles, everyDuration!Date(dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!TimeOfDay(dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!DateTime(dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!SysTime(dur!"hnsecs"(1)))); + + //Verify Examples. + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDuration!Date(dur!"days"(8)); + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 2)); //Using PopFirst.yes would have made this Date(2010, 9, 10). + + range.popFront(); + assert(range.front == Date(2010, 9, 10)); + + range.popFront(); + assert(range.front == Date(2010, 9, 18)); + + range.popFront(); + assert(range.front == Date(2010, 9, 26)); + + range.popFront(); + assert(range.empty); +} + + +/++ + Generates a range-generating function for intervals. + + Returns a delegate which returns the next time point which is the given + number of years, month, and duration later. + + The difference between this version of $(D everyDuration()) and the + version which just takes a $(D Duration) is that this one also takes the + number of years and months (along with an AllowDayOverflow to indicate + whether adding years and months should allow the days to overflow). + + Note that if iterating forward, $(D add!"years"()) is called on the given + time point, then $(D add!"months"()), and finally the duration is added + to it. However, if iterating backwards, the duration is added first, then + $(D add!"months"()) is called, and finally $(D add!"years"()) is called. + That way, going backwards generates close to the same time points that + iterating forward does, but since adding years and months is not entirely + reversible (due to possible day overflow, regardless of whether + AllowDayOverflow.yes or AllowDayOverflow.no is used), it can't be + guaranteed that iterating backwards will give you the same time points as + iterating forward would have (even assuming that the end of the range is a + time point which would be returned by the delegate when iterating forward + from begin). + + Params: + dir = The direction to iterate in. If passing the return + value to $(D fwdRange()), use $(D Direction.fwd). + If passing it to $(D bwdRange()), use $(D Direction.bwd). + years = The number of years to add to the time point passed + to the delegate. + months = The number of months to add to the time point passed to + the delegate. + allowOverflow = Whether the days should be allowed to overflow on begin + and end, causing their month to increment. + duration = The duration to add to the time point passed to the + delegate. + + Examples: +-------------------- +auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); +auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); +auto range = interval.fwdRange(func); + +assert(range.front == Date(2010, 9, 2)); //Using PopFirst.yes would have made this Date(2014, 10, 12). + +range.popFront(); +assert(range.front == Date(2014, 10, 4)); + +range.popFront(); +assert(range.front == Date(2018, 11, 6)); + +range.popFront(); +assert(range.front == Date(2022, 12, 8)); + +range.popFront(); +assert(range.empty); +-------------------- + +/ +static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) + (int years, + int months = 0, + AllowDayOverflow allowOverflow = AllowDayOverflow.yes, + D duration = dur!"days"(0)) nothrow + if(isTimePoint!TP && + __traits(compiles, TP.init + duration) && + __traits(compiles, TP.init.add!"years"(years)) && + __traits(compiles, TP.init.add!"months"(months)) && + (dir == Direction.fwd || dir == Direction.bwd)) +{ + TP func(in TP tp) + { + static if(dir == Direction.fwd) + { + TP retval = cast(TP)tp; + + retval.add!"years"(years, allowOverflow); + retval.add!"months"(months, allowOverflow); + + return retval + duration; + } + else + { + TP retval = tp - duration; + + retval.add!"months"(-months, allowOverflow); + retval.add!"years"(-years, allowOverflow); + + return retval; + } + } + + return &func; +} + +unittest +{ + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + + assertPred!"=="(funcFwd(Date(2009, 12, 25)), Date(2011, 2, 28)); + assertPred!"=="(funcFwd(Date(2009, 12, 26)), Date(2011, 3, 1)); + assertPred!"=="(funcFwd(Date(2009, 12, 27)), Date(2011, 3, 2)); + assertPred!"=="(funcFwd(Date(2009, 12, 28)), Date(2011, 3, 3)); + assertPred!"=="(funcFwd(Date(2009, 12, 29)), Date(2011, 3, 4)); + + assertPred!"=="(funcBwd(Date(2011, 2, 28)), Date(2009, 12, 25)); + assertPred!"=="(funcBwd(Date(2011, 3, 1)), Date(2009, 12, 26)); + assertPred!"=="(funcBwd(Date(2011, 3, 2)), Date(2009, 12, 27)); + assertPred!"=="(funcBwd(Date(2011, 3, 3)), Date(2009, 12, 28)); + assertPred!"=="(funcBwd(Date(2011, 3, 4)), Date(2010, 1, 1)); + } + + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + + assertPred!"=="(funcFwd(Date(2009, 12, 25)), Date(2011, 2, 28)); + assertPred!"=="(funcFwd(Date(2009, 12, 26)), Date(2011, 3, 1)); + assertPred!"=="(funcFwd(Date(2009, 12, 27)), Date(2011, 3, 2)); + assertPred!"=="(funcFwd(Date(2009, 12, 28)), Date(2011, 3, 3)); + assertPred!"=="(funcFwd(Date(2009, 12, 29)), Date(2011, 3, 3)); + + assertPred!"=="(funcBwd(Date(2011, 2, 28)), Date(2009, 12, 25)); + assertPred!"=="(funcBwd(Date(2011, 3, 1)), Date(2009, 12, 26)); + assertPred!"=="(funcBwd(Date(2011, 3, 2)), Date(2009, 12, 27)); + assertPred!"=="(funcBwd(Date(2011, 3, 3)), Date(2009, 12, 28)); + assertPred!"=="(funcBwd(Date(2011, 3, 4)), Date(2010, 1, 1)); + } + + static assert(__traits(compiles, everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + + //Verify Examples. + auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); + auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 2)); //Using PopFirst.yes would have made this Date(2014, 10, 12). + + range.popFront(); + assert(range.front == Date(2014, 10, 4)); + + range.popFront(); + assert(range.front == Date(2018, 11, 6)); + + range.popFront(); + assert(range.front == Date(2022, 12, 8)); + + range.popFront(); + assert(range.empty); +} + + +//TODO Add function to create a range generating function based on a date recurrence pattern string. +// This may or may not involve creating a date recurrence pattern class of some sort - probably +// yes if we want to make it easy to build them. However, there is a standard recurrence +// pattern string format which we'd want to support with a range generator (though if we have +// the class/struct, we'd probably want a version of the range generating function which took +// that rather than a string). + + +//============================================================================== +// Section with ranges. +//============================================================================== + + +/++ + A range over an $(D Interval). + + $(D IntervalRange) is only ever constructed by $(D Interval). However, when + it is constructed, it is given a function, $(D func), which is used to generate + the time points which are iterated over. $(D func()) takes a time point and + returns a time point of the same type. So, for instance, if you had an + $(D Interval!Date), and you wanted to iterate over all of the days in that + interval, you would pass a function to $(D Interval)'s $(D fwdRange()) where + that function took a $(D Date) and returned a $(D Date) which was one day + later. That function would then be used by $(D IntervalRange)'s $(D popFront()) + to iterate over the $(D Date)s in the interval. + + If $(D dir == Direction.fwd), then a range iterates forward in time, while if + $(D dir == Direction.bwd), then it iterates backwards in time. So, if + $(D dir == Direction.fwd) then $(D front == interval.begin), while if + $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must generate + a time point going in the proper direction of iteration, or a + $(D DateTimeException) will be thrown. So, if you're iterating forward in time, + the time point that func generates must be later in time than the one passed + to it. If it's either identical or earlier in time, then a $(D DateTimeException) + will be thrown. If you're iterating backwards, then the generated time point + must be before the time point which was passed in. + + If the generated time point is ever passed the edge of the range in the proper + direction, then the edge of that range will be used instead. So, if iterating + forward, and the generated time point is past the interval's end, then front + becomes end. If iterating backwards, and the generated time point is before + begin, then front becomes begin. In either case, the range would then be empty. + + Also note that while normally the begin of an interval is included in it and + its end is excluded from it, if $(D dir == Direction.bwd), then begin is + treated as excluded and end is treated as included. This allows for the same + behavior in both directions. This works because none of $(D Interval)'s functions + which care about whether begin or end is included or excluded are ever called + by $(D IntervalRange). $(D interval) returns a normal interval, regardless of + whether $(D dir == Direction.fwd) or if $(D dir == Direction.bwd), so any + $(D Interval) functions which are called on it which care about whether begin + or end are included or excluded will treat begin as included and end as excluded. + +/ +struct IntervalRange(TP, Direction dir) + if(isTimePoint!TP && dir != Direction.both) +{ +public: + + /++ + Params: + rhs = The IntervalRange to assign to this one. + +/ + /+ref+/ IntervalRange opAssign(ref IntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + + return this; + } + + + /++ + Whether this IntervalRange is empty. + +/ + @property bool empty() const pure nothrow + { + return _interval.empty; + } + + + /++ + The first time point in the range. + + Throws: + DateTimeException if empty is true. + +/ + @property TP front() const pure + { + _enforceNotEmpty(); + + static if(dir == Direction.fwd) + return _interval.begin; + else + return _interval.end; + } + + + /++ + Pops front from the range, using $(D func()) to generate the next time + point in the range. If the generated time point is beyond the edge of + the range, then front is set to that edge, end the range is then empty. + So, if iterating forwards, and the generated time point is greater + than the interval's end, then front is set to end. If iterating + backwards, and the generated time point is less than the interval's + begin, then front is set to begin. + + Throws: + DateTimeException if empty is true or if the generated time point is + in the wrong direction (i.e. if you're iterating forward and the + generated time point is before front, or if you're iterating + backwards, and the generated time point is after front). + +/ + void popFront() + { + _enforceNotEmpty(); + + static if(dir == Direction.fwd) + { + auto begin = _func(_interval.begin); + + if(begin > _interval.end) + begin = _interval.end; + + _enforceCorrectDirection(begin); + + _interval.begin = begin; + } + else + { + auto end = _func(_interval.end); + + if(end < _interval.begin) + end = _interval.begin; + + _enforceCorrectDirection(end); + + _interval.end = end; + } + } + + + /++ + Returns a copy of $(D this). + +/ + @property IntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this IntervalRange currently covers. + +/ + @property Interval!TP interval() const pure nothrow + { + return cast(Interval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + + /++ + The Direction that this range iterates in. + +/ + @property Direction direction() const pure nothrow + { + return dir; + } + + +private: + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are iterated over. + +/ + this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /++ + Throws: + DateTimeException if empty is true. + +/ + void _enforceNotEmpty(size_t line = __LINE__) const pure + { + if(empty) + throw new DateTimeException("Invalid operation for an empty IntervalRange.", __FILE__, line); + } + + + /++ + Throws: + DateTimeException if newTP is in the wrong direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + static if(dir == Direction.fwd) + { + enforce(newTP > _interval._begin, + new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", + interval._begin, + newTP), + __FILE__, + line)); + } + else + { + enforce(newTP < _interval._end, + new DateTimeException(format("Generated time point is after previous end: prev [%s] new [%s]", + interval._end, + newTP), + __FILE__, + line)); + } + } + + + Interval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. +unittest +{ + static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); + static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); + + static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); + static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); + + static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); + static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); + static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); + static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); +} + +//Test construction of IntervalRange. +unittest +{ + { + Date dateFunc(in Date date) + { + return date; + } + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) + { + return tod; + } + + auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); + + auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) + { + return dt; + } + + auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); + + auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) + { + return cast(SysTime)st; + } + + auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + + auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); + } +} + +//Test IntervalRange's empty(). +unittest +{ + //fwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + + assert(!range.empty); + range.popFront(); + assert(range.empty); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.empty)); + + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } + + //bwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + + assert(!range.empty); + range.popFront(); + assert(range.empty); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.empty)); + + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } +} + +//Test IntervalRange's front. +unittest +{ + //fwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assertPred!"=="(range.front, Date(2010, 7, 4)); + + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertPred!"=="(poppedRange.front, Date(2010, 7, 7)); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); + } + + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assertPred!"=="(range.front, Date(2012, 1, 7)); + + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertPred!"=="(poppedRange.front, Date(2012, 1, 4)); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); + } +} + +//Test IntervalRange's popFront(). +unittest +{ + //fwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach(date; range) + { + assertPred!"=="(date, expected); + expected += dur!"days"(7); + } + + assertPred!"=="(walkLength(range), 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); + } + + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach(date; range) + { + assertPred!"=="(date, expected); + expected += dur!"days"(-7); + } + + assertPred!"=="(walkLength(range), 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); + } +} + +//Test IntervalRange's save. +unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.save == range); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.save == range); + } +} + +//Test IntervalRange's interval. +unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + static assert(__traits(compiles, cRange.interval)); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + static assert(__traits(compiles, cRange.interval)); + } +} + +//Test IntervalRange's func. +unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.func == func); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.func == func); + } +} + +//Test IntervalRange's direction. +unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.direction == Direction.fwd); + + const cRange = range; + static assert(__traits(compiles, cRange.direction)); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.direction == Direction.bwd); + + const cRange = range; + static assert(__traits(compiles, cRange.direction)); + } +} + + +/++ + A range over a $(D PosInfInterval). It is an infinite range. + + $(D PosInfIntervalRange) is only ever constructed by $(D PosInfInterval). + However, when it is constructed, it is given a function, $(D func()), which + is used to generate the time points which are iterated over. $(D func()) + takes a time point and returns a time point of the same type. So, for instance, + if you had a $(D PosInfInterval!Date), and you wanted to iterate over all + of the days in that interval, you would pass a function to $(D PosInfInterval)'s + $(D fwdRange()) where that function took a $(D Date) and returned a $(D Date) + which was one day later. That function would then be used by + $(D PosInfIntervalRange)'s $(D popFront()) to iterate over the $(D Date)s in + the interval. Though obviously, since the range is infinite, you would use a + function such as $(D std.range.take()) with it rather than iterating over + *all* of the dates. + + As the interval goes to positive infinity, the range is always iterated over + forwards, never backwards. $(D func()) must generate a time point going in + the proper direction of iteration, or a $(D DateTimeException) will be thrown. + So, the time points that $(D func()) generates must be later in time than + the one passed to it. If it's either identical or earlier in time, then a + $(D DateTimeException) will be thrown. + +/ +struct PosInfIntervalRange(TP) + if(isTimePoint!TP) +{ +public: + + /++ + Params: + rhs = The PosInfIntervalRange to assign to this one. + +/ + /+ref+/ PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + + return this; + } + + + /++ + This is an infinite range, so it is never empty. + +/ + enum bool empty = false; + + + /++ + The first time point in the range. + +/ + @property TP front() const pure nothrow + { + return _interval.begin; + } + + + /++ + Pops front from the range, using func to generate the next time point + in the range. + + Throws: + DateTimeException if the generated time point is less than than + front. + +/ + void popFront() + { + auto begin = _func(_interval.begin); + + _enforceCorrectDirection(begin); + + _interval.begin = begin; + } + + + /++ + Returns a copy of $(D this). + +/ + @property PosInfIntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this range currently covers. + +/ + @property PosInfInterval!TP interval() const pure nothrow + { + return cast(PosInfInterval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + +private: + + /++ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /++ + Throws: + DateTimeException if newTP is in the wrong direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + enforce(newTP > _interval._begin, + new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", + interval._begin, + newTP), + __FILE__, + line)); + } + + + PosInfInterval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. +unittest +{ + static assert(isInputRange!(PosInfIntervalRange!Date)); + static assert(isForwardRange!(PosInfIntervalRange!Date)); + static assert(isInfinite!(PosInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); + static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); + static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); + static assert(!hasLength!(PosInfIntervalRange!Date)); + static assert(!hasSlicing!(PosInfIntervalRange!Date)); + + static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); + static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); +} + +//Test construction of PosInfIntervalRange. +unittest +{ + { + Date dateFunc(in Date date) + { + return date; + } + + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) + { + return tod; + } + + auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); + + auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) + { + return dt; + } + + auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); + + auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) + { + return cast(SysTime)st; + } + + auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); + + auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); + } +} + +//Test PosInfIntervalRange's front. +unittest +{ + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assertPred!"=="(range.front, Date(2010, 7, 4)); + + auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertPred!"=="(poppedRange.front, Date(2010, 7, 7)); + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); +} + +//Test PosInfIntervalRange's popFront(). +unittest +{ + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach(date; take(range, 79)) + { + assertPred!"=="(date, expected); + expected += dur!"days"(7); + } + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); +} + +//Test PosInfIntervalRange's save. +unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.save == range); +} + +//Test PosInfIntervalRange's interval. +unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + static assert(__traits(compiles, cRange.interval)); +} + +//Test PosInfIntervalRange's func. +unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.func == func); +} + + +/++ + A range over a $(D NegInfInterval). It is an infinite range. + + $(D NegInfIntervalRange) is only ever constructed by $(D NegInfInterval). + However, when it is constructed, it is given a function, $(D func()), which + is used to generate the time points which are iterated over. $(D func()) + takes a time point and returns a time point of the same type. So, for instance, + if you had a $(D NegInfInterval!Date), and you wanted to iterate over all + of the days in that interval, you would pass a function to $(D NegInfInterval)'s + $(D bwdRange()) where that function took a $(D Date) and returned a $(D Date) + which was one day earlier. That function would then be used by + $(D NegInfIntervalRange)'s $(D popFront()) to iterate over the $(D Date)s in + the interval. Though obviously, since the range is infinite, you would use a + function such as $(D std.range.take()) with it rather than iterating over + *all* of the dates. + + As the interval goes to negative infinity, the range is always iterated over + backwards, never forwards. $(D func()) must generate a time point going in + the proper direction of iteration, or a $(D DateTimeException) will be thrown. + So, the time points that $(D func()) generates must be earlier in time than + the one passed to it. If it's either identical or later in time, then a + $(D DateTimeException) will be thrown. + + Also note that while normally the end of an interval is excluded from it, + $(D NegInfIntervalRange) treats it as if it were included. This allows for + the same behavior as you get with $(D PosInfIntervalRange). This works + because none of $(D NegInfInterval)'s functions which care about whether + end is included or excluded are ever called by $(D NegInfIntervalRange). + $(D interval) returns a normal interval, so any $(D NegInfInterval) + functions which are called on it which care about whether end is included + or excluded will treat end as excluded. + +/ +struct NegInfIntervalRange(TP) + if(isTimePoint!TP) +{ +public: + + /++ + Params: + rhs = The NegInfIntervalRange to assign to this one. + +/ + /+ref+/ NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + + return this; + } + + + /++ + This is an infinite range, so it is never empty. + +/ + enum bool empty = false; + + + /++ + The first time point in the range. + +/ + @property TP front() const pure nothrow + { + return _interval.end; + } + + + /++ + Pops front from the range, using func to generate the next time point + in the range. + + Throws: + DateTimeException if the generated time point is greater than than + front. + +/ + void popFront() + { + auto end = _func(_interval.end); + + _enforceCorrectDirection(end); + + _interval.end = end; + } + + + /++ + Returns a copy of $(D this). + +/ + @property NegInfIntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this range currently covers. + +/ + @property NegInfInterval!TP interval() const pure nothrow + { + return cast(NegInfInterval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + +private: + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + DateTimeException if newTP is in the wrong direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + enforce(newTP < _interval._end, + new DateTimeException(format("Generated time point is before previous end: prev [%s] new [%s]", + interval._end, + newTP), + __FILE__, + line)); + } + + + NegInfInterval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. +unittest +{ + static assert(isInputRange!(NegInfIntervalRange!Date)); + static assert(isForwardRange!(NegInfIntervalRange!Date)); + static assert(isInfinite!(NegInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); + static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); + static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); + static assert(!hasLength!(NegInfIntervalRange!Date)); + static assert(!hasSlicing!(NegInfIntervalRange!Date)); + + static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); +} + +//Test construction of NegInfIntervalRange. +unittest +{ + { + Date dateFunc(in Date date) + { + return date; + } + + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) + { + return tod; + } + + auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); + + auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) + { + return dt; + } + + auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); + + auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) + { + return cast(SysTime)(st); + } + + auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + + auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); + } +} + +//Test NegInfIntervalRange's front. +unittest +{ + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assertPred!"=="(range.front, Date(2012, 1, 7)); + + auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertPred!"=="(poppedRange.front, Date(2012, 1, 4)); + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); +} + +//Test NegInfIntervalRange's popFront(). +unittest +{ + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach(date; take(range, 79)) + { + assertPred!"=="(date, expected); + expected += dur!"days"(-7); + } + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); +} + +//Test NegInfIntervalRange's save. +unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.save == range); +} + +//Test NegInfIntervalRange's interval. +unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + static assert(__traits(compiles, cRange.interval)); +} + +//Test NegInfIntervalRange's func. +unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.func == func); +} + + +//============================================================================== +// Section with time zones. +//============================================================================== + +/++ + Represents a time zone. It is used with SysTime to indicate the time zone of + a SysTime. + +/ +abstract class TimeZone +{ +public: + + /++ + The name of the time zone per the TZ database. This is the name used to + get a TimeZone by name with TimeZone.getTimeZone(). + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + +/ + @property string name() const nothrow + { + return _name; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Standard Time). Regardless, it is not the same as name. + +/ + @property string stdName() const nothrow + { + return _stdName; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Daylight Time). Regardless, it is not the same as name. + +/ + @property string dstName() const nothrow + { + return _dstName; + } + + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current dates + but will still return true for hasDST because the time zone did at some + point have DST. + +/ + @property abstract bool hasDST() const nothrow; + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and returns whether DST is effect in + this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time zone. + +/ + abstract bool dstInEffect(long stdTime) const nothrow; + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + abstract long utcToTZ(long stdTime) const nothrow; + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January 1st, + 1 A.D. in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + abstract long tzToUTC(long adjTime) const nothrow; + + + /++ + Returns a TimeZone with the give name per the TZ database. + + This returns a PosixTimeZone on Posix systems and a WindowsTimeZone on + Windows systems. If you want a PosixTimeZone on Windows, then call + $(D PosixTimeZone.getTimeZone()) directly and give it the location of + the TZ database time zone files on disk. + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + + Params: + name = The TZ database name of the time zone that you're looking for. + + Throws: + DateTimeException if the given time zone could not be found. + + Examples: +-------------------- +auto tz = TimeZone.getTimeZone("America/Los_Angeles"); +-------------------- + +/ + static immutable(TimeZone) getTimeZone(string name) + { + version(Posix) + return PosixTimeZone.getTimeZone(name); + else version(Windows) + return WindowsTimeZone.getTimeZone(name); + } + + unittest + { + //Verify Example. + auto tz = TimeZone.getTimeZone("America/Los_Angeles"); + } + + + /++ + Returns a list of the names of the time zones installed on the system. + + You can provide a sub-name to narrow down the list of time zones (which + will likely be in the thousands if you get them all). For example, + if you pass in "America" as the sub-name, then only the time zones which + begin with "America" will be returned. + + Params: + subname = The first part of the time zones that you want. + + Throws: + FileException on Posix systems if it fails to read from disk. + DateTimeException on Windows systems if it fails to read the registry. + +/ + static string[] getInstalledTZNames(string subname = "") + { + version(Posix) + return PosixTimeZone.getInstalledTZNames(subname); + else version(Windows) + return WindowsTimeZone.getInstalledTZNames(subname); + } + + +private: + + /++ + Params: + name = The TZ database name for the time zone. + stdName = The abbreviation for the time zone during std time. + dstName = The abbreviation for the time zone during DST. + +/ + this(string name, string stdName, string dstName) immutable pure + { + _name = name; + _stdName = stdName; + _dstName = dstName; + } + + + immutable string _name; + immutable string _stdName; + immutable string _dstName; +} + + +version(Posix) +{ +//This should be in core.stdc.time, but it isn't, so +//we're declaring it here. +extern(C) +{ + extern __gshared char* tzname[2]; + extern __gshared int daylight; +} +} + + +/++ + A TimeZone which represents the current local time zone on + the system running your program. + + This uses the underlying C calls to adjust the time rather + than using specific D code based off of system settings to calculate + the time such as PosixTimeZone and WindowsTimeZone do. That also + means that it will use whatever the current time zone is on the system, + even if the system's time zone changes while the program is running. + +/ +final class LocalTime : TimeZone +{ +public: + + /++ + LocalTime is a singleton class. $(D LocalTime()) returns its only instance. + +/ + static immutable(LocalTime) opCall() pure nothrow + { + return _localTime; + } + + + version(D_Ddoc) + { + /++ + The name of the time zone per the TZ database. This is the name used to + get a TimeZone by name with $(D TimeZone.getTimeZone()). + + Note that this always returns the empty string. This is because time + zones cannot be uniquely identified by the attributes given by the OS + (such as the stdName and dstName), and neither Posix systems nor + Windows systems provide an easy way to get the TZ database name of + the local time zone. + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + +/ + @property override string name() const nothrow + { + return super.name; + } + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Standard Time). Regardless, it is not the same as name. + + This property is overridden because the local time of the system could + change while the program is running and we need to determine it dynamically + rather than it being fixed like it would be with most time zones. + +/ + @property override string stdName() const nothrow + { + version(Posix) + { + try + return to!string(tzname[0]); + catch(Exception e) + assert(0, "to!string(tzname[0]) failed."); + } + else version(Windows) + { + try + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + //Cannot use to!string() like this should, probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 + //return to!string(tzInfo.StandardName); + + wchar[32] str; + + foreach(i, ref wchar c; str) + c = tzInfo.StandardName[i]; + + string retval; + + foreach(dchar c; str) + { + if(c == '\0') + break; + + retval ~= c; + } + + return retval; + } + catch(Exception e) + assert(0, "GetTimeZoneInformation() threw."); + } + } + + unittest + { + assert(LocalTime().stdName !is null); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assertPred!"=="(LocalTime().stdName, "PST"); + + setTZEnvVar("America/New_York"); + assertPred!"=="(LocalTime().stdName, "EST"); + } + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Daylight Time). Regardless, it is not the same as name. + + This property is overridden because the local time of the system could + change while the program is running and we need to determine it + dynamically rather than it being fixed like it would be with most time + zones. + +/ + @property override string dstName() const nothrow + { + version(Posix) + { + try + return to!string(tzname[1]); + catch(Exception e) + assert(0, "to!string(tzname[0]) failed."); + } + else version(Windows) + { + try + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + //Cannot use to!string() like this should, probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 + //return to!string(tzInfo.DaylightName); + + wchar[32] str; + + foreach(i, ref wchar c; str) + c = tzInfo.DaylightName[i]; + + string retval; + + foreach(dchar c; str) + { + if(c == '\0') + break; + + retval ~= c; + } + + return retval; + } + catch(Exception e) + assert(0, "GetTimeZoneInformation() threw."); + } + } + + unittest + { + assert(LocalTime().dstName !is null); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assertPred!"=="(LocalTime().dstName, "PDT"); + + setTZEnvVar("America/New_York"); + assertPred!"=="(LocalTime().dstName, "EDT"); + } + } + + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current + dates but will still return true for hasDST because the time zone did + at some point have DST. + +/ + @property override bool hasDST() const nothrow + { + version(Posix) + return cast(bool)(daylight); + else version(Windows) + { + try + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return tzInfo.DaylightDate.wMonth != 0; + } + catch(Exception e) + assert(0, "GetTimeZoneInformation() threw."); + } + } + + unittest + { + LocalTime().hasDST; + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().hasDST); + + setTZEnvVar("America/New_York"); + assert(LocalTime().hasDST); + } + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and returns whether DST is in + effect in this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time zone. + +/ + override bool dstInEffect(long stdTime) const nothrow + { + time_t unixTime = stdTimeToUnixTime(stdTime); + + version(Posix) + { + tm* timeInfo = localtime(&unixTime); + + return cast(bool)(timeInfo.tm_isdst); + } + else version(Windows) + { + //Apparently Windows isn't smart enough to deal with negative time_t. + if(unixTime >= 0) + { + tm* timeInfo = localtime(&unixTime); + + if(timeInfo) + return cast(bool)(timeInfo.tm_isdst); + } + + TIME_ZONE_INFORMATION tzInfo; + + try + GetTimeZoneInformation(&tzInfo); + catch(Exception e) + assert(0, "The impossible happened. GetTimeZoneInformation() threw."); + + return WindowsTimeZone._dstInEffect(&tzInfo, stdTime); + } + } + + unittest + { + auto currTime = Clock.currStdTime; + LocalTime().dstInEffect(currTime); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + auto std = SysTime(DateTime(2010, 1, 1, 12, 0, 0), LocalTime()); + auto dst = SysTime(DateTime(2010, 7, 1, 12, 0, 0), LocalTime()); + + setTZEnvVar("America/Los_Angeles"); + assert(!LocalTime().dstInEffect(std.stdTime)); + assert(LocalTime().dstInEffect(dst.stdTime)); + assert(!std.dstInEffect); + assert(dst.dstInEffect); + + setTZEnvVar("America/New_York"); + assert(!LocalTime().dstInEffect(std.stdTime)); + assert(LocalTime().dstInEffect(dst.stdTime)); + assert(!std.dstInEffect); + assert(dst.dstInEffect); + } + } + + + /++ + Returns hnsecs in the local time zone using the standard C function calls + on Posix systems and the standard Windows system calls on Windows systems + to adjust the time to the appropriate time zone from std time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + + See_Also: + TimeZone.utcToTZ() + +/ + override long utcToTZ(long stdTime) const nothrow + { + version(Posix) + { + time_t unixTime = stdTimeToUnixTime(stdTime); + tm* timeInfo = localtime(&unixTime); + + return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + + try + GetTimeZoneInformation(&tzInfo); + catch(Exception e) + assert(0, "GetTimeZoneInformation() threw."); + + return WindowsTimeZone._utcToTZ(&tzInfo, stdTime, hasDST); + } + } + + unittest + { + LocalTime().utcToTZ(0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + { + setTZEnvVar("America/Los_Angeles"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assertPred!"=="(LocalTime().utcToTZ(std.stdTime), SysTime(DateTime(2009, 12, 31, 16, 0, 0)).stdTime); + assertPred!"=="(LocalTime().utcToTZ(dst.stdTime), SysTime(DateTime(2010, 6, 30, 17, 0, 0)).stdTime); + } + + { + setTZEnvVar("America/New_York"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assertPred!"=="(LocalTime().utcToTZ(std.stdTime), SysTime(DateTime(2009, 12, 31, 19, 0, 0)).stdTime); + assertPred!"=="(LocalTime().utcToTZ(dst.stdTime), SysTime(DateTime(2010, 6, 30, 20, 0, 0)).stdTime); + } + } + } + + + /++ + Returns std time using the standard C function calls on Posix systems + and the standard Windows system calls on Windows systems to adjust the + time to UTC from the appropriate time zone. + + See_Also: + TimeZone.tzToUTC() + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) const nothrow + { + version(Posix) + { + time_t unixTime = stdTimeToUnixTime(adjTime); + tm* timeInfo = localtime(&unixTime); + + return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + + try + GetTimeZoneInformation(&tzInfo); + catch(Exception e) + assert(0, "GetTimeZoneInformation() threw."); + + return WindowsTimeZone._tzToUTC(&tzInfo, adjTime, hasDST); + } + } + + unittest + { + LocalTime().tzToUTC(0); + assertPred!"=="(LocalTime().tzToUTC(LocalTime().utcToTZ(0)), 0); + assertPred!"=="(LocalTime().utcToTZ(LocalTime().tzToUTC(0)), 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + { + setTZEnvVar("America/Los_Angeles"); + auto std = SysTime(DateTime(2009, 12, 31, 16, 0, 0)); + auto dst = SysTime(DateTime(2010, 6, 30, 17, 0, 0)); + assertPred!"=="(LocalTime().tzToUTC(std.stdTime), SysTime(Date(2010, 1, 1)).stdTime); + assertPred!"=="(LocalTime().tzToUTC(dst.stdTime), SysTime(Date(2010, 7, 1)).stdTime); + } + + { + setTZEnvVar("America/New_York"); + auto std = SysTime(DateTime(2009, 12, 31, 19, 0, 0)); + auto dst = SysTime(DateTime(2010, 6, 30, 20, 0, 0)); + assertPred!"=="(LocalTime().tzToUTC(std.stdTime), SysTime(Date(2010, 1, 1)).stdTime); + assertPred!"=="(LocalTime().tzToUTC(dst.stdTime), SysTime(Date(2010, 7, 1)).stdTime); + } + } + } + + +private: + + this() immutable pure + { + super("", "", ""); + } + + + static immutable LocalTime _localTime; + + + static this() + { + tzset(); + + _localTime = new immutable(LocalTime)(); + } +} + + +/++ + A TimeZone which represents UTC. + +/ +final class UTC : TimeZone +{ +public: + + /++ + UTC is a singleton class. $(D UTC()) returns its only instance. + +/ + static immutable(UTC) opCall() pure nothrow + { + return _utc; + } + + + /++ + Always returns false. + +/ + @property override bool hasDST() const nothrow + { + return false; + } + + + /++ + Always returns false. + +/ + override bool dstInEffect(long stdTime) const nothrow + { + return false; + } + + + /++ + Returns the given hnsecs without changing them at all. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + + See_Also: + TimeZone.utcToTZ() + +/ + override long utcToTZ(long stdTime) const nothrow + { + return stdTime; + } + + unittest + { + assertPred!"=="(UTC().utcToTZ(0), 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assertPred!"=="(UTC().utcToTZ(std.stdTime), std.stdTime); + assertPred!"=="(UTC().utcToTZ(dst.stdTime), dst.stdTime); + } + } + + + /++ + Returns the given hnsecs without changing them at all. + + See_Also: + TimeZone.tzToUTC() + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) const nothrow + { + return adjTime; + } + + unittest + { + assertPred!"=="(UTC().tzToUTC(0), 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assertPred!"=="(UTC().tzToUTC(std.stdTime), std.stdTime); + assertPred!"=="(UTC().tzToUTC(dst.stdTime), dst.stdTime); + } + } + + +private: + + this() immutable pure + { + super("UTC", "UTC", "UTC"); + } + + + static immutable UTC _utc; + + + static this() + { + _utc = new immutable(UTC)(); + } +} + + +/++ + Represents a time zone with an offset (in minutes, west is negative) from + UTC but no DST. + + It's primarily used as the time zone in the result of $(D SysTime)'s + $(D fromISOString()), $(D fromISOExtendedString()), and + $(D fromSimpleString()). + + $(D name) and $(D dstName) are always the empty string since this time zone + has no DST and while it may be meant to represent a time zone which is in + the TZ database, obviously it's not likely to be following the exact rules + of any of the time zones in the TZ database, so it makes no sense to set it. + +/ +final class SimpleTimeZone : TimeZone +{ +public: + + /++ + Always returns false. + +/ + @property override bool hasDST() const nothrow + { + return false; + } + + + /++ + Always returns false. + +/ + override bool dstInEffect(long stdTime) const nothrow + { + return false; + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and converts it to this time + zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time + zone's time. + +/ + override long utcToTZ(long stdTime) const nothrow + { + return stdTime + convert!("minutes", "hnsecs")(utcOffset); + } + + unittest + { + assertPred!"=="((new SimpleTimeZone(-8 * 60)).utcToTZ(0), -288_000_000_000L); + assertPred!"=="((new SimpleTimeZone(8 * 60)).utcToTZ(0), 288_000_000_000L); + assertPred!"=="((new SimpleTimeZone(-8 * 60)).utcToTZ(54_321_234_567_890L), 54_033_234_567_890L); + auto stz = new SimpleTimeZone(-8 * 60); + const cstz = new SimpleTimeZone(-8 * 60); + static assert(__traits(compiles, stz.utcToTZ(50002))); + static assert(__traits(compiles, cstz.utcToTZ(50002))); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in this time zone's time and converts it to UTC + (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) const nothrow + { + return adjTime - convert!("minutes", "hnsecs")(utcOffset); + } + + unittest + { + assertPred!"=="((new SimpleTimeZone(-8 * 60)).tzToUTC(-288_000_000_000L), 0); + assertPred!"=="((new SimpleTimeZone(8 * 60)).tzToUTC(288_000_000_000L), 0); + assertPred!"=="((new SimpleTimeZone(-8 * 60)).tzToUTC(54_033_234_567_890L), 54_321_234_567_890L); + + auto stz = new SimpleTimeZone(-8 * 60); + const cstz = new SimpleTimeZone(-8 * 60); + static assert(__traits(compiles, stz.tzToUTC(20005))); + static assert(__traits(compiles, cstz.tzToUTC(20005))); + } + + + /++ + Params: + utcOffset = This time zone's offset from UTC in minutes with west + of UTC being negative (it is added to UTC to get the + adjusted time). + stdName = The stdName for this time zone. + +/ + this(int utcOffset, string stdName = "") immutable + { + enforce(abs(utcOffset) < 1440, new DateTimeException("Offset from UTC must be within range (-24:00 - 24:00).")); + + super("", stdName, ""); + + this.utcOffset = utcOffset; + } + + unittest + { + auto stz = new SimpleTimeZone(-8 * 60, "PST"); + + assertPred!"=="(stz.name, ""); + assertPred!"=="(stz.stdName, "PST"); + assertPred!"=="(stz.dstName, ""); + assertPred!"=="(stz.utcOffset, -8 * 60); + } + + + /++ + The number of minutes the offset from UTC is (negative is west of UTC, + positive is east). + +/ + immutable int utcOffset; + +private: + + /++ + Returns a time zone as a string with an offset from UTC. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + + Params: + utcOffset = The number of minutes offset from UTC (negative means + west). + +/ + static string toISOString(int utcOffset) + { + immutable absOffset = abs(utcOffset); + enforce(absOffset < 1440, new DateTimeException("Offset from UTC must be within range (-24:00 - 24:00).")); + + immutable hours = convert!("minutes", "hours")(absOffset); + immutable minutes = absOffset - convert!("hours", "minutes")(hours); + + if(utcOffset < 0) + return format("-%02d:%02d", hours, minutes); + + return format("+%02d:%02d", hours, minutes); + } + + unittest + { + static string testSTZInvalid(int offset) + { + return SimpleTimeZone.toISOString(offset); + } + + assertThrown!DateTimeException(testSTZInvalid(1440)); + assertThrown!DateTimeException(testSTZInvalid(-1440)); + + assertPred!"=="(toISOString(0), "+00:00"); + assertPred!"=="(toISOString(1), "+00:01"); + assertPred!"=="(toISOString(10), "+00:10"); + assertPred!"=="(toISOString(59), "+00:59"); + assertPred!"=="(toISOString(60), "+01:00"); + assertPred!"=="(toISOString(90), "+01:30"); + assertPred!"=="(toISOString(120), "+02:00"); + assertPred!"=="(toISOString(480), "+08:00"); + assertPred!"=="(toISOString(1439), "+23:59"); + + assertPred!"=="(toISOString(-1), "-00:01"); + assertPred!"=="(toISOString(-10), "-00:10"); + assertPred!"=="(toISOString(-59), "-00:59"); + assertPred!"=="(toISOString(-60), "-01:00"); + assertPred!"=="(toISOString(-90), "-01:30"); + assertPred!"=="(toISOString(-120), "-02:00"); + assertPred!"=="(toISOString(-480), "-08:00"); + assertPred!"=="(toISOString(-1439), "-23:59"); + } + + + /++ + Takes a time zone as a string with an offset from UTC and returns a + $(D SimpleTimeZone) which matches. + + The accepted formats for time zone offsets are +H, -H, +HH, -HH, +H:MM, + -H:MM, +HH:MM, and -HH:MM. + + Params: + isoString = A string which represents a time zone in the ISO format. + +/ + static immutable(SimpleTimeZone) fromISOString(S)(S isoString) + if(isSomeString!(S)) + { + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.startsWith("-") || dstr.startsWith("+"), new DateTimeException("Invalid ISO String")); + + auto sign = dstr.startsWith("-") ? -1 : 1; + + dstr.popFront(); + enforce(!dstr.empty, new DateTimeException("Invalid ISO String")); + + immutable colon = dstr.indexOf(":"); + + dstring hoursStr; + dstring minutesStr; + + if(colon != -1) + { + hoursStr = dstr[0 .. colon]; + minutesStr = dstr[colon + 1 .. $]; + enforce(minutesStr.length == 2, new DateTimeException(format("Invalid ISO String: %s", dstr))); + } + else + hoursStr = dstr; + + enforce(!canFind!((dchar c){return !isdigit(c);})(hoursStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); + enforce(!canFind!((dchar c){return !isdigit(c);})(minutesStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); + + immutable hours = to!int(hoursStr); + immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr); + + return new SimpleTimeZone(sign * cast(int)(convert!("hours", "minutes")(hours) + minutes)); + } + + unittest + { + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("Z")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-:")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+:")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-1:")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+1:")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("1")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("-24:00")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+24:00")); + assertThrown!DateTimeException(SimpleTimeZone.fromISOString("+1:0")); + + assertPred!"=="(SimpleTimeZone.fromISOString("+00:00").utcOffset, (new SimpleTimeZone(0)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+00:01").utcOffset, (new SimpleTimeZone(1)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+00:10").utcOffset, (new SimpleTimeZone(10)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+00:59").utcOffset, (new SimpleTimeZone(59)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+01:00").utcOffset, (new SimpleTimeZone(60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+01:30").utcOffset, (new SimpleTimeZone(90)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+02:00").utcOffset, (new SimpleTimeZone(120)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+08:00").utcOffset, (new SimpleTimeZone(480)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+23:59").utcOffset, (new SimpleTimeZone(1439)).utcOffset); + + assertPred!"=="(SimpleTimeZone.fromISOString("-00:01").utcOffset, (new SimpleTimeZone(-1)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-00:10").utcOffset, (new SimpleTimeZone(-10)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-00:59").utcOffset, (new SimpleTimeZone(-59)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-01:00").utcOffset, (new SimpleTimeZone(-60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-01:30").utcOffset, (new SimpleTimeZone(-90)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-02:00").utcOffset, (new SimpleTimeZone(-120)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-08:00").utcOffset, (new SimpleTimeZone(-480)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-23:59").utcOffset, (new SimpleTimeZone(-1439)).utcOffset); + + assertPred!"=="(SimpleTimeZone.fromISOString("+0").utcOffset, (new SimpleTimeZone(0)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+1").utcOffset, (new SimpleTimeZone(60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+2").utcOffset, (new SimpleTimeZone(120)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+23").utcOffset, (new SimpleTimeZone(1380)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+2").utcOffset, (new SimpleTimeZone(120)).utcOffset); + + assertPred!"=="(SimpleTimeZone.fromISOString("+0").utcOffset, (new SimpleTimeZone(0)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+1").utcOffset, (new SimpleTimeZone(60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+2").utcOffset, (new SimpleTimeZone(120)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+23").utcOffset, (new SimpleTimeZone(1380)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+1:00").utcOffset, (new SimpleTimeZone(60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("+1:01").utcOffset, (new SimpleTimeZone(61)).utcOffset); + + assertPred!"=="(SimpleTimeZone.fromISOString("-0").utcOffset, (new SimpleTimeZone(0)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-1").utcOffset, (new SimpleTimeZone(-60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-2").utcOffset, (new SimpleTimeZone(-120)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-23").utcOffset, (new SimpleTimeZone(-1380)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-1:00").utcOffset, (new SimpleTimeZone(-60)).utcOffset); + assertPred!"=="(SimpleTimeZone.fromISOString("-1:01").utcOffset, (new SimpleTimeZone(-61)).utcOffset); + } + + //Test that converting from an ISO string to a SimpleTimeZone to an ISO String works properly. + unittest + { + static void testSTZ(in string isoString, int expectedOffset, size_t line = __LINE__) + { + auto stz = SimpleTimeZone.fromISOString(isoString); + assertPred!"=="(stz.utcOffset, expectedOffset, "", __FILE__, line); + + auto result = SimpleTimeZone.toISOString(stz.utcOffset); + assertPred!"=="(result, isoString, "", __FILE__, line); + } + + testSTZ("+00:00", 0); + testSTZ("+00:01", 1); + testSTZ("+00:10", 10); + testSTZ("+00:59", 59); + testSTZ("+01:00", 60); + testSTZ("+01:30", 90); + testSTZ("+02:00", 120); + testSTZ("+08:00", 480); + testSTZ("+08:00", 480); + testSTZ("+23:59", 1439); + + testSTZ("-00:01", -1); + testSTZ("-00:10", -10); + testSTZ("-00:59", -59); + testSTZ("-01:00", -60); + testSTZ("-01:30", -90); + testSTZ("-02:00", -120); + testSTZ("-08:00", -480); + testSTZ("-08:00", -480); + testSTZ("-23:59", -1439); + } +} + + +/++ + Represents a time zone from a TZ database time zone file. Files from the TZ + database are how Posix systems hold their time zone information. Unfortunately, + Windows does not use the TZ database, though it does name its time zones + using the TZ database names. You can, however, use PosixTimeZone (which reads + its information from the TZ database files on disk) on Windows if you provide + the TZ database files + ( Repository with the TZ database files (tzdata) ) + yourself and tell $(D PosixTimeZone.getTimeZone()) where the directory holding + them is. + + TZ database files hold DST transitions for a large interval of the time + covered by time_t. So, barring errors in the information in the TZ database + files, it will use the correct DST rules for any date. Windows, on the other + hand, maintains only the current DST rules, so historical dates will use the + current DST rules (and therefore potentially be incorrect). So, if you want + the DST rules that you use to be more accurate, or if you're looking for your + program to act consistently on both Posix and Windows systems, then, as + mentioned above, you'll need to include the TZ database files with your program + and give $(D PosixTimeZone.getTimeZone()) the directory on disk where they + are located. + + To get a PosixTimeZone, either call $(D PosixTimeZone.getTimeZone()) (which + will allow you to specify the location the time zone files) or call + $(D TimeZone.getTimeZone()) (which will give you a PosixTimeZone on Posix + systems and a WindowsTimeZone on Windows systems). + + Note: + Unless your system's local time zone deals with leap seconds (which is + highly unlikely), then only way that you will get a time zone which takes + leap seconds into account is if you use PosixTimeZone with a time zone + whose name starts with "right/". Those time zone files do include leap + seconds, and PosixTimeZone will take them into account (though posix + systems which use a "right/" time zone as their local time zone will + *not* take leap seconds into account even though they're in the file). + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + +/ +final class PosixTimeZone : TimeZone +{ +public: + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current + dates but will still return true for hasDST because the time zone did + at some point have DST. + +/ + @property override bool hasDST() const nothrow + { + return _hasDST; + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and returns whether DST is in + effect in this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time zone. + +/ + override bool dstInEffect(long stdTime) const nothrow + { + assert(!_transitions.empty); + + try + { + immutable unixTime = stdTimeToUnixTime(stdTime); + + if(_transitions.front.timeT >= unixTime) + return _transitions.front.ttInfo.isDST; + + //Okay, casting is a hack, but indexOf shouldn't be changing it, and it would be + //too inefficient to have to keep duping it every time we have to calculate the time. + //Hopefully, indexOf will properly support immutable ranges at some point. + auto found = std.algorithm.indexOf!"b < a.timeT"(cast(Transition[])_transitions, unixTime); + + if(found == -1) + return _transitions.back.ttInfo.isDST; + + auto transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return transition.ttInfo.isDST; + } + catch(Exception e) + assert(0, format("Nothing in calculateLeapSeconds() should be throwing. Caught Exception: %s", e)); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's time. + +/ + override long utcToTZ(long stdTime) const nothrow + { + assert(!_transitions.empty); + + try + { + immutable leapSecs = calculateLeapSeconds(stdTime); + immutable unixTime = stdTimeToUnixTime(stdTime); + + if(_transitions.front.timeT >= unixTime) + return stdTime + convert!("seconds", "hnsecs")(_transitions.front.ttInfo.utcOffset + leapSecs); + + //Okay, casting is a hack, but indexOf shouldn't be changing it, and it would be + //too inefficient to have to keep duping it every time we have to calculate the time. + //Hopefully, indexOf will properly support immutable ranges at some point. + auto found = std.algorithm.indexOf!"b < a.timeT"(cast(Transition[])_transitions, unixTime); + + if(found == -1) + return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + auto transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); + } + catch(Exception e) + assert(0, format("Nothing in calculateLeapSeconds() should be throwing. Caught Exception: %s", e)); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in this time zone's time and converts it to UTC + (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to UTC time. + +/ + override long tzToUTC(long adjTime) const nothrow + { + assert(!_transitions.empty); + + try + { + immutable leapSecs = calculateLeapSeconds(adjTime); + immutable unixTime = stdTimeToUnixTime(adjTime); + + if(_transitions.front.timeT >= unixTime) + return adjTime - convert!("seconds", "hnsecs")(_transitions.front.ttInfo.utcOffset + leapSecs); + + //Okay, casting is a hack, but indexOf shouldn't be changing it, and it would be + //too inefficient to have to keep duping it every time we have to calculate the time. + //Hopefully, indexOf will properly support immutable ranges at some point. + auto found = std.algorithm.indexOf!"b < a.timeT"(cast(Transition[])_transitions, unixTime); + + if(found == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + auto transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); + } + catch(Exception e) + assert(0, format("Nothing in calculateLeapSeconds() should be throwing. Caught Exception: %s", e)); + } + + + version(Posix) + /// The default directory where the TZ database files are. It's empty for Windows, since Windows doesn't have them. + enum defaultTZDatabaseDir = "/usr/share/zoneinfo/"; + else version(Windows) + /// The default directory where the TZ database files are. It's empty for Windows, since Windows doesn't have them. + enum defaultTZDatabaseDir = ""; + + + /++ + Returns a TimeZone with the give name per the TZ database. The time zone + information is fetched from the TZ database time zone files in the given + directory. + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + + Params: + name = The TZ database name of the time zone that you're + looking for. + tzDatabaseDir = The directory where the TZ database files are located. + Because these files are not located on Windows systems, + you will need to provide them yourself and give their + location here if you wish to use PosixTimeZones. + + Throws: + DateTimeException if the given time zone could not be found or + FileException if the TZ database file could not be opened. + + Examples: +-------------------- +auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); + +assert(tz.name == "America/Los_Angeles"); +assert(tz.stdName == "PST"); +assert(tz.dstName == "PDT"); +-------------------- + +/ + //TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed + // directory. + static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) + { + name = strip(name); + + enforce(tzDatabaseDir.exists, new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); + enforce(tzDatabaseDir.isdir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); + + version(Posix) + auto file = tzDatabaseDir ~ sep ~ name; + else version(Windows) + auto file = tzDatabaseDir ~ sep ~ replace(strip(name), "/", sep); + + enforce(file.exists, new DateTimeException(format("File %s does not exist.", file))); + enforce(file.isfile, new DateTimeException(format("%s is not a file.", file))); + + auto tzFile = File(file); + immutable gmtZone = file.canFind("GMT"); + + try + { + _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); + + immutable char tzFileVersion = readVal!char(tzFile); + _enforceValidTZFile(tzFileVersion == '\0' || tzFileVersion == '2'); + + { + auto zeroBlock = readVal!(ubyte[])(tzFile, 15); + bool allZeroes = true; + + foreach(val; zeroBlock) + { + if(val != 0) + { + allZeroes = false; + break; + } + } + + _enforceValidTZFile(allZeroes); + } + + + //The number of UTC/local indicators stored in the file. + auto tzh_ttisgmtcnt = readVal!int(tzFile); + + //The number of standard/wall indicators stored in the file. + auto tzh_ttisstdcnt = readVal!int(tzFile); + + //The number of leap seconds for which data is stored in the file. + auto tzh_leapcnt = readVal!int(tzFile); + + //The number of "transition times" for which data is stored in the file. + auto tzh_timecnt = readVal!int(tzFile); + + //The number of "local time types" for which data is stored in the file (must not be zero). + auto tzh_typecnt = readVal!int(tzFile); + _enforceValidTZFile(tzh_typecnt != 0); + + //The number of characters of "timezone abbreviation strings" stored in the file. + auto tzh_charcnt = readVal!int(tzFile); + + //time_ts where DST transitions occur. + auto transitionTimeTs = new long[](tzh_timecnt); + foreach(ref transition; transitionTimeTs) + transition = readVal!int(tzFile); + + //Indices into ttinfo structs indicating the changes + //to be made at the corresponding DST transition. + auto ttInfoIndices = new ubyte[](tzh_timecnt); + foreach(ref ttInfoIndex; ttInfoIndices) + ttInfoIndex = readVal!ubyte(tzFile); + + //ttinfos which give info on DST transitions. + auto tempTTInfos = new TempTTInfo[](tzh_typecnt); + foreach(ref ttInfo; tempTTInfos) + ttInfo = readVal!TempTTInfo(tzFile); + + //The array of time zone abbreviation characters. + auto tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); + + auto leapSeconds = new LeapSecond[](tzh_leapcnt); + foreach(ref leapSecond; leapSeconds) + { + //The time_t when the leap second occurs. + auto timeT = readVal!int(tzFile); + + //The total number of leap seconds to be applied after + //the corresponding leap second. + auto total = readVal!int(tzFile); + + leapSecond = LeapSecond(timeT, total); + } + + //Indicate whether each corresponding DST transition were specified + //in standard time or wall clock time. + auto transitionIsStd = new bool[](tzh_ttisstdcnt); + foreach(ref isStd; transitionIsStd) + isStd = readVal!bool(tzFile); + + //Indicate whether each corresponding DST transition associated with + //local time types are specified in UTC or local time. + auto transitionInUTC = new bool[](tzh_ttisgmtcnt); + foreach(ref inUTC; transitionInUTC) + inUTC = readVal!bool(tzFile); + + _enforceValidTZFile(!tzFile.eof()); + + //If version 2, the information is duplicated in 64-bit. + if(tzFileVersion == '2') + { + _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); + + _enforceValidTZFile(readVal!(char)(tzFile) == '2'); + + { + auto zeroBlock = readVal!(ubyte[])(tzFile, 15); + bool allZeroes = true; + + foreach(val; zeroBlock) + { + if(val != 0) + { + allZeroes = false; + break; + } + } + + _enforceValidTZFile(allZeroes); + } + + + //The number of UTC/local indicators stored in the file. + tzh_ttisgmtcnt = readVal!int(tzFile); + + //The number of standard/wall indicators stored in the file. + tzh_ttisstdcnt = readVal!int(tzFile); + + //The number of leap seconds for which data is stored in the file. + tzh_leapcnt = readVal!int(tzFile); + + //The number of "transition times" for which data is stored in the file. + tzh_timecnt = readVal!int(tzFile); + + //The number of "local time types" for which data is stored in the file (must not be zero). + tzh_typecnt = readVal!int(tzFile); + _enforceValidTZFile(tzh_typecnt != 0); + + //The number of characters of "timezone abbreviation strings" stored in the file. + tzh_charcnt = readVal!int(tzFile); + + //time_ts where DST transitions occur. + transitionTimeTs = new long[](tzh_timecnt); + foreach(ref transition; transitionTimeTs) + transition = readVal!long(tzFile); + + //Indices into ttinfo structs indicating the changes + //to be made at the corresponding DST transition. + ttInfoIndices = new ubyte[](tzh_timecnt); + foreach(ref ttInfoIndex; ttInfoIndices) + ttInfoIndex = readVal!ubyte(tzFile); + + //ttinfos which give info on DST transitions. + tempTTInfos = new TempTTInfo[](tzh_typecnt); + foreach(ref ttInfo; tempTTInfos) + ttInfo = readVal!TempTTInfo(tzFile); + + //The array of time zone abbreviation characters. + tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); + + leapSeconds = new LeapSecond[](tzh_leapcnt); + foreach(ref leapSecond; leapSeconds) + { + //The time_t when the leap second occurs. + auto timeT = readVal!long(tzFile); + + //The total number of leap seconds to be applied after + //the corresponding leap second. + auto total = readVal!int(tzFile); + + leapSecond = LeapSecond(timeT, total); + } + + //Indicate whether each corresponding DST transition were specified + //in standard time or wall clock time. + transitionIsStd = new bool[](tzh_ttisstdcnt); + foreach(ref isStd; transitionIsStd) + isStd = readVal!bool(tzFile); + + //Indicate whether each corresponding DST transition associated with + //local time types are specified in UTC or local time. + transitionInUTC = new bool[](tzh_ttisgmtcnt); + foreach(ref inUTC; transitionInUTC) + inUTC = readVal!bool(tzFile); + } + + _enforceValidTZFile(tzFile.readln().strip().empty()); + + auto posixEnvStr = tzFile.readln().strip(); + + _enforceValidTZFile(tzFile.readln().strip().empty()); + _enforceValidTZFile(tzFile.eof()); + + + auto transitionTypes = new TransitionType*[](tempTTInfos.length); + + foreach(i, ref ttype; transitionTypes) + { + bool isStd = false; + + if(i < transitionIsStd.length && !transitionIsStd.empty) + isStd = transitionIsStd[i]; + + bool inUTC = false; + + if(i < transitionInUTC.length && !transitionInUTC.empty) + inUTC = transitionInUTC[i]; + + ttype = new TransitionType(isStd, inUTC); + } + + auto ttInfos = new immutable(TTInfo)*[](tempTTInfos.length); + foreach(i, ref ttInfo; ttInfos) + { + auto tempTTInfo = tempTTInfos[i]; + + if(gmtZone) + tempTTInfo.tt_gmtoff = -tempTTInfo.tt_gmtoff; + + auto abbrevChars = tzAbbrevChars[tempTTInfo.tt_abbrind .. $]; + string abbrev = abbrevChars[0 .. std.string.indexOf(abbrevChars, "\0")].idup; + + ttInfo = new immutable(TTInfo)(tempTTInfos[i], abbrev); + } + + auto tempTransitions = new TempTransition[](transitionTimeTs.length); + foreach(i, ref tempTransition; tempTransitions) + { + immutable ttiIndex = ttInfoIndices[i]; + auto transitionTimeT = transitionTimeTs[i]; + auto ttype = transitionTypes[ttiIndex]; + auto ttInfo = ttInfos[ttiIndex]; + + tempTransition = TempTransition(transitionTimeT, ttInfo, ttype); + } + + if(tempTransitions.empty) + { + _enforceValidTZFile(ttInfos.length == 1 && transitionTypes.length == 1); + tempTransitions ~= TempTransition(0, ttInfos[0], transitionTypes[0]); + } + + sort!"a.timeT < b.timeT"(tempTransitions); + sort!"a.timeT < b.timeT"(leapSeconds); + + auto transitions = new Transition[](tempTransitions.length); + foreach(i, ref transition; transitions) + { + auto tempTransition = tempTransitions[i]; + auto transitionTimeT = tempTransition.timeT; + auto ttInfo = tempTransition.ttInfo; + auto ttype = tempTransition.ttype; + + _enforceValidTZFile(i == 0 || transitionTimeT > tempTransitions[i - 1].timeT); + + transition = Transition(transitionTimeT, ttInfo); + } + + string stdName; + string dstName; + bool hasDST = false; + + foreach(transition; retro(transitions)) + { + auto ttInfo = transition.ttInfo; + + if(ttInfo.isDST) + { + if(dstName.empty) + dstName = ttInfo.abbrev; + + hasDST = true; + } + else + { + if(stdName.empty) + stdName = ttInfo.abbrev; + } + + if(!stdName.empty && !dstName.empty) + break; + } + + return new PosixTimeZone(transitions.idup, leapSeconds.idup, name, stdName, dstName, hasDST); + } + catch(DateTimeException dte) + throw dte; + catch(Exception e) + throw new DateTimeException("Not a valid TZ data file", __FILE__, __LINE__, e); + } + + + //Since reading in the time zone files could be expensive, most unit tests + //are consolidated into this one unittest block which minimizes how often it + //reads a time zone file. + unittest + { + version(Posix) + { + scope(exit) clearTZEnvVar(); + + //Test for "America/Los_Angeles". + { + //Verify Example for getTimeZone(). + auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); + + assert(tz.name == "America/Los_Angeles"); + assert(tz.stdName == "PST"); + assert(tz.dstName == "PDT"); + assert(tz.hasDST); + + //Continue testing passed example. + auto std = SysTime(DateTime(2010, 1, 1, 12, 0, 0), tz); + auto dst = SysTime(DateTime(2010, 7, 1, 12, 0, 0), tz); + + assert(!std.dstInEffect); + assert(dst.dstInEffect); + + setTZEnvVar("America/Los_Angeles"); + { + time_t unixTime = std.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = std.toTM(); + + assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); + assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); + assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); + assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); + assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); + assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); + assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); + assertPred!"=="(to!string(ourTimeInfo.tm_zone), to!string(osTimeInfo.tm_zone)); + } + + { + time_t unixTime = dst.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = dst.toTM(); + + assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); + assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); + assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); + assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); + assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); + assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); + assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); + assertPred!"=="(to!string(ourTimeInfo.tm_zone), to!string(osTimeInfo.tm_zone)); + } + + auto leapTZ = PosixTimeZone.getTimeZone("right/America/Los_Angeles"); + + assert(leapTZ.name == "right/America/Los_Angeles"); + assert(leapTZ.stdName == "PST"); + assert(leapTZ.dstName == "PDT"); + assert(leapTZ.hasDST); + + auto leapSTD = SysTime(std.stdTime, leapTZ); + auto leapDST = SysTime(dst.stdTime, leapTZ); + + assert(!leapSTD.dstInEffect); + assert(leapDST.dstInEffect); + + assertPred!"=="(leapSTD.stdTime, std.stdTime); + assertPred!"=="(leapDST.stdTime, dst.stdTime); + + assertPred!"=="(leapSTD.adjTime - convert!("seconds", "hnsecs")(24), std.adjTime); + assertPred!"=="(leapDST.adjTime - convert!("seconds", "hnsecs")(24), dst.adjTime); + } + + //Test for "Europe/Paris". + { + auto tz = PosixTimeZone.getTimeZone("Europe/Paris"); + + assertPred!"=="(tz.name, "Europe/Paris"); + assertPred!"=="(tz.stdName, "CET"); + assertPred!"=="(tz.dstName, "CEST"); + assertPred!"=="(tz.hasDST, true); + + auto std = SysTime(DateTime(2010, 1, 1, 12, 0, 0), tz); + auto dst = SysTime(DateTime(2010, 7, 1, 12, 0, 0), tz); + + assert(!std.dstInEffect); + assert(dst.dstInEffect); + + setTZEnvVar("Europe/Paris"); + { + time_t unixTime = std.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = std.toTM(); + + assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); + assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); + assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); + assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); + assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); + assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); + assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); + assertPred!"=="(to!string(ourTimeInfo.tm_zone), to!string(osTimeInfo.tm_zone)); + } + + { + time_t unixTime = dst.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = dst.toTM(); + + assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); + assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); + assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); + assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); + assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); + assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); + assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); + assertPred!"=="(to!string(ourTimeInfo.tm_zone), to!string(osTimeInfo.tm_zone)); + } + + auto leapTZ = PosixTimeZone.getTimeZone("right/Europe/Paris"); + + assert(leapTZ.name == "right/Europe/Paris"); + assert(leapTZ.stdName == "CET"); + assert(leapTZ.dstName == "CEST"); + assert(leapTZ.hasDST); + + auto leapSTD = SysTime(std.stdTime, leapTZ); + auto leapDST = SysTime(dst.stdTime, leapTZ); + + assert(!leapSTD.dstInEffect); + assert(leapDST.dstInEffect); + + assertPred!"=="(leapSTD.stdTime, std.stdTime); + assertPred!"=="(leapDST.stdTime, dst.stdTime); + + assertPred!"=="(leapSTD.adjTime - convert!("seconds", "hnsecs")(24), std.adjTime); + assertPred!"=="(leapDST.adjTime - convert!("seconds", "hnsecs")(24), dst.adjTime); + } + + //Test for "UTC". + { + auto tz = PosixTimeZone.getTimeZone("UTC"); + + assertPred!"=="(tz.name, "UTC"); + assertPred!"=="(tz.stdName, "UTC"); + assertPred!"=="(tz.dstName, "UTC"); + assertPred!"=="(tz.hasDST, false); + + auto std = SysTime(DateTime(2010, 1, 1, 12, 0, 0), tz); + auto dst = SysTime(DateTime(2010, 7, 1, 12, 0, 0), tz); + + assert(!std.dstInEffect); + assert(!dst.dstInEffect); + + setTZEnvVar("UTC"); + { + time_t unixTime = std.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = std.toTM(); + + assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); + assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); + assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); + assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); + assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); + assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); + assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); + assertPred!"=="(to!string(ourTimeInfo.tm_zone), to!string(osTimeInfo.tm_zone)); + } + + { + time_t unixTime = dst.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = dst.toTM(); + + assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); + assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); + assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); + assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); + assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); + assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); + assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); + assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); + assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); + assertPred!"=="(to!string(ourTimeInfo.tm_zone), to!string(osTimeInfo.tm_zone)); + } + + auto leapTZ = PosixTimeZone.getTimeZone("right/UTC"); + + assert(leapTZ.name == "right/UTC"); + assert(leapTZ.stdName == "UTC"); + assert(leapTZ.dstName == "UTC"); + assert(!leapTZ.hasDST); + + auto leapSTD = SysTime(std.stdTime, leapTZ); + auto leapDST = SysTime(dst.stdTime, leapTZ); + + assert(!leapSTD.dstInEffect); + assert(!leapDST.dstInEffect); + + assertPred!"=="(leapSTD.stdTime, std.stdTime); + assertPred!"=="(leapDST.stdTime, dst.stdTime); + + assertPred!"=="(leapSTD.adjTime - convert!("seconds", "hnsecs")(24), std.adjTime); + assertPred!"=="(leapDST.adjTime - convert!("seconds", "hnsecs")(24), dst.adjTime); + } + } + } + + + /++ + Returns a list of the names of the time zones installed on the system. + + You can provide a sub-name to narrow down the list of time zones (which + will likely be in the thousands if you get them all). For example, + if you pass in "America" as the sub-name, then only the time zones which + begin with "America" will be returned. + + Params: + subname = The first part of the time zones that you want. + + Throws: + FileException if it fails to read from disk. + +/ + static string[] getInstalledTZNames(string subname = "", string tzDatabaseDir = defaultTZDatabaseDir) + { + version(Posix) + subname = strip(subname); + else version(Windows) + subname = replace(strip(subname), "/", sep); + + if(!tzDatabaseDir.endsWith(sep)) + tzDatabaseDir ~= sep; + + enforce(tzDatabaseDir.exists, new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); + enforce(tzDatabaseDir.isdir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); + + auto timezones = appender!(string[])(); + + foreach(DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth)) + { + if(dentry.isfile) + { + auto tzName = dentry.name[tzDatabaseDir.length .. $]; + + if(!tzName.getExt().empty()) + continue; + + if(!tzName.startsWith(subname)) + continue; + + timezones.put(tzName); + } + } + + sort(timezones.data); + + return timezones.data; + } + + unittest + { + version(Posix) + { + static void testPTZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + + PosixTimeZone.getTimeZone(tzName); + } + + static void testPTZFailure(string tzName) + { + scope(success) writefln("TZName which was supposed to throw: %s", tzName); + + PosixTimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + + foreach(tzName; tzNames) + assertNotThrown!DateTimeException(testPTZSuccess(tzName)); + + foreach(DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) + { + if(dentry.isfile) + { + auto tzName = dentry.name[defaultTZDatabaseDir.length .. $]; + + if(!canFind(tzNames, tzName)) + assertThrown!DateTimeException(testPTZFailure(tzName)); + } + } + } + } + + +private: + + /++ + Holds information on when a time transition occures (usually a + transition to or from DST) as well as a pointer to the + TTInfo which holds information on the utc offset passed the + transition. + +/ + struct Transition + { + this(long timeT, immutable (TTInfo)* ttInfo) + { + this.timeT = timeT; + this.ttInfo = ttInfo; + } + + long timeT; + immutable (TTInfo)* ttInfo; + } + + + /++ + Holds information on when a leap second occurs. + +/ + struct LeapSecond + { + this(long timeT, int total) + { + this.timeT = timeT; + this.total = total; + } + + long timeT; + int total; + } + + /++ + Holds information on the utc offset after a transition as well as + whether DST is in effect after that transition. + +/ + struct TTInfo + { + this(in TempTTInfo tempTTInfo, string abbrev) immutable + { + utcOffset = tempTTInfo.tt_gmtoff; + isDST = tempTTInfo.tt_isdst; + this.abbrev = abbrev; + } + + immutable int utcOffset; /// Offset from UTC. + immutable bool isDST; /// Whether DST is in effect. + immutable string abbrev; /// The current abbreviation for the time zone. + } + + + /++ + Struct used to hold information relating to TTInfo while organizing + the time zone information prior to putting it in its final form. + +/ + struct TempTTInfo + { + this(int gmtOff, bool isDST, ubyte abbrInd) + { + tt_gmtoff = gmtOff; + tt_isdst = isDST; + tt_abbrind = abbrInd; + } + + int tt_gmtoff; + bool tt_isdst; + ubyte tt_abbrind; + } + + + /++ + Struct used to hold information relating to Transition while organizing + the time zone information prior to putting it in its final form. + +/ + struct TempTransition + { + this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) + { + this.timeT = timeT; + this.ttInfo = ttInfo; + this.ttype = ttype; + } + + long timeT; + immutable (TTInfo)* ttInfo; + TransitionType* ttype; + } + + + /++ + Struct used to hold information relating to Transition and TTInfo while + organizing the time zone information prior to putting it in its final form. + +/ + struct TransitionType + { + this(bool isStd, bool inUTC) + { + this.isStd = isStd; + this.inUTC = inUTC; + } + + bool isStd; /// Whether the transition is in std time (as opposed to wall clock time). + bool inUTC; /// Whether the transition is in UTC (as opposed to local time). + } + + + /++ + Reads an int from a TZ file. + +/ + static T readVal(T)(ref File tzFile) + if(is(T == int)) + { + T[1] buff; + + _enforceValidTZFile(!tzFile.eof()); + tzFile.rawRead(buff); + + return cast(int)ntohl(buff[0]); + } + + + /++ + Reads a long from a TZ file. + +/ + static T readVal(T)(ref File tzFile) + if(is(T == long)) + { + T[1] buff; + + _enforceValidTZFile(!tzFile.eof()); + tzFile.rawRead(buff); + + return cast(long)ntoh64(buff[0]); + } + + /++ + Reads an array of values from a TZ file. + +/ + static T readVal(T)(ref File tzFile, size_t length) + if(isArray!T) + { + auto buff = new T(length); + + _enforceValidTZFile(!tzFile.eof()); + tzFile.rawRead(buff); + + return buff; + } + + + /++ + Reads a TempTTInfo from a TZ file. + +/ + static T readVal(T)(ref File tzFile) + if(is(T == TempTTInfo)) + { + return TempTTInfo(readVal!int(tzFile), + readVal!bool(tzFile), + readVal!ubyte(tzFile)); + } + + + /++ + Reads a value from a TZ file. + +/ + static T readVal(T)(ref File tzFile) + if(!is(T == int) && + !is(T == long) && + !is(T == char[]) && + !is(T == TempTTInfo)) + { + T[1] buff; + + _enforceValidTZFile(!tzFile.eof()); + tzFile.rawRead(buff); + + return buff[0]; + } + + /++ + 64 bit version of ntoh. Unfortunately, for some reason, most systems + provide only 16 and 32 bit versions of this, so we need to provide it + ourselves. We really should declare a version of this in core somewhere. + +/ + static ulong ntoh64(ulong val) + { + static if(endian == Endian.LittleEndian) + return endianSwap64(val); + else + return val; + } + + + /++ + Swaps the endianness of a 64-bit value. We really should declare a + version of this in core somewhere. + +/ + static ulong endianSwap64(ulong val) + { + return ((val & 0xff00000000000000UL) >> 56) | + ((val & 0x00ff000000000000UL) >> 40) | + ((val & 0x0000ff0000000000UL) >> 24) | + ((val & 0x000000ff00000000UL) >> 8) | + ((val & 0x00000000ff000000UL) << 8) | + ((val & 0x0000000000ff0000UL) << 24) | + ((val & 0x000000000000ff00UL) << 40) | + ((val & 0x00000000000000ffUL) << 56); + } + + + /++ + Throws: + DateTimeException if $(D result) is false. + +/ + static void _enforceValidTZFile(bool result, size_t line = __LINE__) + { + if(!result) + throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); + } + + + int calculateLeapSeconds(long stdTime) const nothrow + { + try + { + if(_leapSeconds.empty) + return 0; + + immutable unixTime = stdTimeToUnixTime(stdTime); + + if(_leapSeconds.front.timeT >= unixTime) + return 0; + + //Okay, casting is a hack, but indexOf shouldn't be changing it, and it would be + //too inefficient to have to keep duping it every time we have to calculate the time. + //Hopefully, indexOf will properly support immutable ranges at some point. + auto found = std.algorithm.indexOf!"b < a.timeT"(cast(LeapSecond[])_leapSeconds, unixTime); + + if(found == -1) + return _leapSeconds.back.total; + + auto leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; + + return leapSecond.total; + } + catch(Exception e) + assert(0, format("Nothing in calculateLeapSeconds() should be throwing. Caught Exception: %s", e)); + } + + + this(immutable Transition[] transitions, + immutable LeapSecond[] leapSeconds, + string name, + string stdName, + string dstName, + bool hasDST) immutable + { + if(dstName.empty && !stdName.empty) + dstName = stdName; + else if(stdName.empty && !dstName.empty) + stdName = dstName; + + super(name, stdName, dstName); + + if(!transitions.empty) + { + foreach(i, transition; transitions[0 .. $-1]) + _enforceValidTZFile(transition.timeT < transitions[i + 1].timeT); + } + + foreach(i, leapSecond; leapSeconds) + _enforceValidTZFile(i == leapSeconds.length - 1 || leapSecond.timeT < leapSeconds[i + 1].timeT); + + _transitions = transitions; + _leapSeconds = leapSeconds; + _hasDST = hasDST; + } + + + immutable Transition[] _transitions; /// List of times when the utc offset changes. + immutable LeapSecond[] _leapSeconds; /// List of leap second occurrences. + immutable bool _hasDST; /// Whether DST is in effect for this time zone at any point in time. +} + + + +version(Windows) +{ + +//Should be in core.sys.windows.windows, but for some reason it isn't. +extern(Windows) +{ +export LONG RegQueryValueExA(HKEY hKey, LPCSTR name, LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count); + +struct REG_TZI_FORMAT +{ + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; +} + +} + +/++ + Represents a time zone from the Windows registry. Unfortunately, Windows + does not use the TZ database, though it does name its time zones using the + TZ database names. You can, however, use PosixTimeZone (which reads its + information from the TZ database files on disk) on Windows if you provide + the TZ database files + ( Repository with the TZ database files (tzdata) ) + yourself and tell $(D PosixTimeZone.getTimeZone()) where the directory + holding them is. + + TZ database files hold DST transitions for a large interval of the time + covered by time_t. So, barring errors in the information in the TZ database + files, it will use the correct DST rules for any date. Windows, on the other + hand, maintains only the current DST rules, so historical dates will use + the current DST rules (and therefore potentially be incorrect). So, if you + want the DST rules that you use to be more accurate, or if you're looking for + your program to act consistently on both Posix and Windows systems, then, as + mentioned above, you'll need to include the TZ database files with your + program and give $(D PosixTimeZone.getTimeZone()) the directory on disk + where they are located. + + However, if all you care about is whether current times use the correct DST + rules, or if you don't care whether the DST rules are historically accurate, + then you can just use WindowsTimeZone on Windows. WindowsTimeZone does not + exist on Posix systems. + + To get a WindowsTimeZone, either call $(D WindowsTimeZone.getTimeZone()) or + call $(D TimeZone.getTimeZone()) (which will give you a PosixTimeZone on Posix + systems and a WindowsTimeZone on Windows systems). + +/ +final class WindowsTimeZone : TimeZone +{ +public: + + /++ + Whether this time zone has Daylight Savings Time at any point in time. Note that for some time zone types + it may not have DST for current dates but will still return true for hasDST because the time zone did at some + point have DST. + +/ + @property override bool hasDST() const nothrow + { + return _tzInfo.DaylightDate.wMonth != 0; + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and returns whether DST is in + effect in this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + override bool dstInEffect(long stdTime) const nothrow + { + return _dstInEffect(&_tzInfo, stdTime); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and converts it to this time + zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's time. + +/ + override long utcToTZ(long stdTime) const nothrow + { + return _utcToTZ(&_tzInfo, stdTime, hasDST); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in this time zone's time and converts it to UTC + (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) const nothrow + { + return _tzToUTC(&_tzInfo, adjTime, hasDST); + } + + + /++ + Returns a TimeZone with the give name per the TZ database. The time zone + information is fetched from the Windows registry. + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + + Params: + name = The TZ database name of the time zone that you're looking for. + + Throws: + DateTimeException if the given time zone could not be found. + + Examples: +-------------------- +auto tz = TimeZone.getTimeZone("America/Los_Angeles"); +-------------------- + +/ + static immutable(WindowsTimeZone) getTimeZone(string name) + { + auto keyStr = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\0"; + HKEY baseKey; + + { + auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyStr.ptr, 0, KEY_READ, &baseKey); + if(result != ERROR_SUCCESS) + throw new DateTimeException(format("Failed to open registry. Error: %s", result)); + } + scope(exit) RegCloseKey(baseKey); + + char[1024] keyName; + auto nameLen = keyName.length; + int result; + for(DWORD index = 0; + (result = RegEnumKeyExA(baseKey, index, keyName.ptr, &nameLen, null, null, null, null)) != ERROR_NO_MORE_ITEMS; + ++index, nameLen = keyName.length) + { + if(result == ERROR_SUCCESS) + { + HKEY tzKey; + if(RegOpenKeyExA(baseKey, keyName.ptr, 0, KEY_READ, &tzKey) == ERROR_SUCCESS) + { + scope(exit) RegCloseKey(tzKey); + char[1024] strVal; + auto strValLen = strVal.length; + + bool queryStringValue(string name, size_t lineNum = __LINE__) + { + strValLen = strVal.length; + + return RegQueryValueExA(tzKey, name.ptr, null, null, cast(ubyte*)strVal.ptr, &strValLen) == ERROR_SUCCESS; + } + + if(queryStringValue("Display\0")) + { + auto displayName = to!string(strVal.ptr); + + if(displayName == name) + { + if(queryStringValue("Std\0")) + { + //Cannot use to!wstring(char*), probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 + static wstring conv(char* cstr, size_t strValLen) + { + cstr[strValLen - 1] = '\0'; + + string retval; + + for(;; ++cstr) + { + if(*cstr == '\0') + break; + + retval ~= *cstr; + } + + return to!wstring(retval); + } + + //auto stdName = to!wstring(strVal.ptr); + auto stdName = conv(strVal.ptr, strValLen); + + if(queryStringValue("Dlt\0")) + { + //auto dstName = to!wstring(strVal.ptr); + auto dstName = conv(strVal.ptr, strValLen); + + enum tzi = "TZI\0"; + REG_TZI_FORMAT binVal; + auto binValLen = REG_TZI_FORMAT.sizeof; + + if(RegQueryValueExA(tzKey, tzi.ptr, null, null, cast(ubyte*)&binVal, &binValLen) == ERROR_SUCCESS) + { + TIME_ZONE_INFORMATION tzInfo; + + auto stdNameLen = stdName.length > 32 ? 32 : stdName.length; + auto dstNameLen = dstName.length > 32 ? 32 : dstName.length; + + tzInfo.Bias = binVal.Bias; + tzInfo.StandardName[0 .. stdNameLen] = stdName[0 .. stdNameLen]; + tzInfo.StandardName[stdNameLen .. $] = '\0'; + tzInfo.StandardDate = binVal.StandardDate; + tzInfo.StandardBias = binVal.StandardBias; + tzInfo.DaylightName[0 .. dstNameLen] = dstName[0 .. dstNameLen]; + tzInfo.DaylightName[dstNameLen .. $] = '\0'; + tzInfo.DaylightDate = binVal.DaylightDate; + tzInfo.DaylightBias = binVal.DaylightBias; + + return new WindowsTimeZone(name, tzInfo); + } + } + } + } + } + } + } + } + + throw new DateTimeException(format("Failed to find time zone: %s", name)); + } + + unittest + { + //Verify Example. + auto tz = TimeZone.getTimeZone("America/Los_Angeles"); + } + + + /++ + Returns a list of the names of the time zones installed on the system. + + You can provide a sub-name to narrow down the list of time zones (which + will likely be in the thousands if you get them all). For example, + if you pass in "America" as the sub-name, then only the time zones which + begin with "America" will be returned. + + Params: + subname = The first part of the time zones that you want. + + Throws: + DateTimeException if it fails to read the registry. + +/ + static string[] getInstalledTZNames(string subname = "") + { + auto timezones = appender!(string[])(); + + auto keyStr = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\0"; + HKEY baseKey; + + { + auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyStr.ptr, 0, KEY_READ, &baseKey); + if(result != ERROR_SUCCESS) + throw new DateTimeException(format("Failed to open registry. Error: %s", result)); + } + scope(exit) RegCloseKey(baseKey); + + char[1024] keyName; + auto nameLen = keyName.length; + int result; + for(DWORD index = 0; + (result = RegEnumKeyExA(baseKey, index, keyName.ptr, &nameLen, null, null, null, null)) != ERROR_NO_MORE_ITEMS; + ++index, nameLen = keyName.length) + { + if(result == ERROR_SUCCESS) + { + HKEY tzKey; + if(RegOpenKeyExA(baseKey, keyName.ptr, 0, KEY_READ, &tzKey) == ERROR_SUCCESS) + { + scope(exit) RegCloseKey(tzKey); + char[1024] strVal; + auto strValLen = strVal.length; + + bool queryStringValue(string name, size_t lineNum = __LINE__) + { + strValLen = strVal.length; + + return RegQueryValueExA(tzKey, name.ptr, null, null, cast(ubyte*)strVal.ptr, &strValLen) == ERROR_SUCCESS; + } + + if(queryStringValue("Display\0")) + { + auto displayName = to!string(strVal.ptr); + + if(displayName.startsWith(subname)) + timezones.put(displayName); + } + } + } + } + + sort(timezones.data); + + return timezones.data; + } + + unittest + { + WindowsTimeZone.getInstalledTZNames(); + } + + +private: + + static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow + { + try + { + if(tzInfo.DaylightDate.wMonth == 0) + return false; + + auto utcDateTime = cast(DateTime)SysTime(stdTime, UTC()); + + //The limits of what SystemTimeToTZSpecificLocalTime() will accept. + if(utcDateTime.year < 1601) + { + if(utcDateTime.month == Month.feb && utcDateTime.day == 29) + utcDateTime.day = 28; + + utcDateTime.year = 1601; + } + else if(utcDateTime.year > 30_827) + { + if(utcDateTime.month == Month.feb && utcDateTime.day == 29) + utcDateTime.day = 28; + + utcDateTime.year = 30_827; + } + + //SystemTimeToTZSpecificLocalTime() doesn't act correctly at the beginning + //or end of the year (bleh). Unless some bizarre time zone changes + //DST on January 1st or December 31st, this should fix the problem. + if(utcDateTime.month == Month.jan) + { + if(utcDateTime.day == 1) + utcDateTime.day = 2; + } + else if(utcDateTime.month == Month.dec && utcDateTime.day == 31) + utcDateTime.day = 30; + + SYSTEMTIME utcTime = void; + SYSTEMTIME otherTime = void; + + utcTime.wYear = utcDateTime.year; + utcTime.wMonth = utcDateTime.month; + utcTime.wDay = utcDateTime.day; + utcTime.wHour = utcDateTime.hour; + utcTime.wMinute = utcDateTime.minute; + utcTime.wSecond = utcDateTime.second; + + SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*)tzInfo, &utcTime, &otherTime); + + immutable otherDateTime = DateTime(otherTime.wYear, + otherTime.wMonth, + otherTime.wDay, + otherTime.wHour, + otherTime.wMinute, + otherTime.wSecond); + immutable diff = utcDateTime - otherDateTime; + immutable minutes = diff.total!"minutes"() - tzInfo.Bias; + + if(minutes == tzInfo.DaylightBias) + return true; + + assert(minutes == tzInfo.StandardBias); + + return false; + } + catch(Exception e) + assert(0, e.toString()); + //assert(0, "DateTime's constructor threw."); + } + + unittest + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(1600, 1, 1)).stdTime); + WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(1601, 1, 1)).stdTime); + WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(30_827, 1, 1)).stdTime); + WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(30_828, 1, 1)).stdTime); + } + + + static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow + { + if(hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) + return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); + + return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); + } + + + static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow + { + if(hasDST) + { + try + { + auto localTime = cast(DateTime)SysTime(adjTime, UTC()); + + DateTime getTransitionTime(const ref SYSTEMTIME transition) + { + DateTime switchDate; + + if(transition.wYear == 0) + switchDate.year = localTime.year; + else + switchDate.year = transition.wYear; + + switchDate.month = cast(Month)transition.wMonth; + + DayOfWeek dow = cast(DayOfWeek)transition.wDayOfWeek; + int occurrence = transition.wDay; + + if(occurrence < 5) + { + Date date = localTime.date; + + date.day = 1; + date.day = cast(int)(date.day + daysToDayOfWeek(date.dayOfWeek, dow) + convert!("weeks", "days")(occurrence - 1)); + switchDate.day = date.day; + } + else + { + Date date = localTime.date.endOfMonth; + + switchDate.day = date.day - daysToDayOfWeek(dow, date.dayOfWeek); + } + + switchDate.hour = transition.wHour; + switchDate.minute = transition.wMinute; + + return switchDate; + } + + auto stdSwitch = getTransitionTime(tzInfo.StandardDate); + auto dstSwitch = getTransitionTime(tzInfo.DaylightDate); + + bool isDST = false; + + if(localTime < stdSwitch) + isDST = localTime < dstSwitch ? stdSwitch < dstSwitch : true; + else + isDST = localTime < dstSwitch ? false : dstSwitch > stdSwitch; + + if(isDST) + return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); + } + catch(Exception e) + assert(0, "SysTime's constructor threw."); + } + + return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); + } + + + this(string name, TIME_ZONE_INFORMATION tzInfo) immutable + { + //Cannot use to!string(wchar*), probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 + static string conv(wchar* wcstr) + { + wcstr[31] = '\0'; + wstring retval; + + for(;; ++wcstr) + { + if(*wcstr == '\0') + break; + + retval ~= *wcstr; + } + + return to!string(retval); + } + + //super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); + super(name, conv(tzInfo.StandardName.ptr), conv(tzInfo.DaylightName.ptr)); + + _tzInfo = tzInfo; + } + + + TIME_ZONE_INFORMATION _tzInfo; +} + +} +else version(D_Ddoc) +{ + +/++ + Represents a time zone from the Windows registry. Unfortunately, Windows + does not use the TZ database, though it does name its time zones using the + TZ database names. You can, however, use PosixTimeZone (which reads its + information from the TZ database files on disk) on Windows if you provide + the TZ database files + ( Repository with the TZ database files (tzdata) ) + yourself and tell $(D PosixTimeZone.getTimeZone()) where the directory + holding them is. + + TZ database files hold DST transitions for a large interval of the time + covered by time_t. So, barring errors in the information in the TZ database + files, it will use the correct DST rules for any date. Windows, on the other + hand, maintains only the current DST rules, so historical dates will use + the current DST rules (and therefore potentially be incorrect). So, if you + want the DST rules that you use to be more accurate, or if you're looking for + your program to act consistently on both Posix and Windows systems, then, as + mentioned above, you'll need to include the TZ database files with your + program and give $(D PosixTimeZone.getTimeZone()) the directory on disk + where they are located. + + However, if all you care about is whether current times use the correct DST + rules, or if you don't care whether the DST rules are historically accurate, + then you can just use WindowsTimeZone on Windows. WindowsTimeZone does not + exist on Posix systems. + + To get a WindowsTimeZone, either call $(D WindowsTimeZone.getTimeZone()) or + call $(D TimeZone.getTimeZone()) (which will give you a PosixTimeZone on Posix + systems and a WindowsTimeZone on Windows systems). + +/ +final class WindowsTimeZone : TimeZone +{ +public: + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current dates + but will still return true for hasDST because the time zone did at some + point have DST. + +/ + @property override bool hasDST() const nothrow + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and returns whether DST is in + effect in this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this + time zone. + +/ + override bool dstInEffect(long stdTime) const nothrow + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in UTC time (i.e. std time) and converts it to this time + zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's time. + +/ + override long utcToTZ(long stdTime) const nothrow + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + Takes the number of hecto-nanoseconds (100 ns) since midnight, January + 1st, 1 A.D. in this time zone's time and converts it to UTC + (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to UTC time. + +/ + override long tzToUTC(long adjTime) const nothrow + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + Returns a TimeZone with the give name per the TZ database. The time zone + information is fetched from the Windows registry. + + See_Also: + Wikipedia entry on TZ database + List of Time Zones + + Params: + name = The TZ database name of the time zone that you're looking for. + + Throws: + DateTimeException if the given time zone could not be found. + + Examples: +-------------------- +auto tz = TimeZone.getTimeZone("America/Los_Angeles"); +-------------------- + +/ + static immutable(WindowsTimeZone) getTimeZone(string name) + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + Returns a list of the names of the time zones installed on the system. + + You can provide a sub-name to narrow down the list of time zones (which + will likely be in the thousands if you get them all). For example, + if you pass in "America" as the sub-name, then only the time zones which + begin with "America" will be returned. + + Params: + subname = The first part of the time zones that you want. + +/ + static string[] getInstalledTZNames(string subname = "") + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + +private: + + this() immutable pure + { + super("", "", ""); + assert(0, "No implementation. Function exists only for DDoc generation"); + } +} + +} + + +version(Posix) +{ + /++ + Allows you to set the local time zone on Posix systems with the TZ + database name by setting the TZ environment variable. + + Unfortunately, there is no way to do it on Windows using the TZ + database name, so this function only exists on Posix systems. + +/ + void setTZEnvVar(string tzDatabaseName) nothrow + { + try + { + char[] env = to!(char[])("TZ=:" ~ PosixTimeZone.defaultTZDatabaseDir ~ tzDatabaseName ~ "\0"); + + putenv(env.ptr); + tzset(); + } + catch(Exception e) + assert(0, "to!(char[]) threw when it shouldn't have."); + } + + + /++ + Clears the TZ environment variable. + + Only on Posix. + +/ + void clearTZEnvVar() nothrow + { + try + { + char[] env = to!(char[])("TZ\0"); + + unsetenv(env.ptr); + tzset(); + } + catch(Exception e) + assert(0, "to!(char[]) threw when it shouldn't have."); + } +} +else version(D_Ddoc) +{ + /++ + Allows you to set the local time zone on Posix systems with the TZ + database name by setting the TZ environment variable. + + Unfortunately, there is no way to do it on Windows using the TZ + database name, so this function only exists on Posix systems. + +/ + void setTZEnvVar(string tzDatabaseName) + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + Clears the TZ environment variable. + + Only on Posix. + +/ + void clearTZEnvVar() + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } +} + + +//============================================================================== +// Section with StopWatch and Benchmark Code. +//============================================================================== + +/++ + StopWatch's AutoStart flag + +/ enum autoStart = AutoStart.yes; -/******************************************************************************* - * StopWatch measures time highly precise as possible. - * - * This class uses performance counter. - * On Windows, This uses QueryPerformanceCounter. - * For Posix, This uses clock_gettime if available, gettimeofday otherwise. - * - * But this has dispersion in accuracy by environment. - * It is impossible to remove this dispersion. This depends on multi task - * system for example overhead from change of the context switch of the thread. - * - * Usage is here: - * Example: - *------------------------------------------------------------------------------ - *void foo() { - * StopWatch sw; - * static immutable N = 100; - * Ticks[N] times; - * Ticks last = Ticks.fromSeconds(0); - * foreach (i; 0..N) { - * sw.start(); // start/resume mesuring. - * foreach (Unused; 0..1000000) bar(); - * sw.stop(); // stop/pause mesuring. - * // Return value of peek() after having stopped are the always same. - * writeln((i+1)*1000000, " times done, lap time: ", - * sw.peek().msec, "[ms]"); - * times[i] = sw.peek() - last; - * last = sw.peek(); - * } - * real sum = 0; - * // When you want to know the number of seconds of the fact, - * // you can use properties of Ticks. - * // (seconds, mseconds, useconds, interval) - * foreach (e; times) sum += e.interval; - * writeln("Average time: ", sum/N, "[s]"); - *} - *------------------------------------------------------------------------------ - */ +/++ + StopWatch measures time as precisely as possible. + + This class uses a high-performance counter. On Windows systems, it uses + $(D QueryPerformanceCounter), and on Posix systems, it uses $(D clock_gettime()) + if available, and $(D gettimeofday()) otherwise. + + But the accuracy of StopWatch differs from system to system. It is impossible + to for it to be the same from system to system since the precision of the + system clock varies from system to system, and other system-dependent and + situation-dependent stuff (such as the overhead of a context switching between + threads) can also affect StopWatch's accuracy. + + Examples: +-------------------- +void foo() { + StopWatch sw; + static immutable N = 100; + TickDuration[N] times; + TickDuration last = TickDuration.from!"seconds"(0); + foreach (i; 0..N) { + sw.start(); // start/resume mesuring. + foreach (Unused; 0..1000000) bar(); + sw.stop(); // stop/pause mesuring. + // Return value of peek() after having stopped are the always same. + writeln((i+1)*1000000, " times done, lap time: ", + sw.peek().msec, "[ms]"); + times[i] = sw.peek() - last; + last = sw.peek(); + } + real sum = 0; + // When you want to know the number of seconds of the fact, + // you can use properties of TickDuration. + // (seconds, mseconds, useconds, hnsecs) + foreach (e; times) sum += e.hnsecs; + writeln("Average time: ", sum/N, " hnsecs"); +} +-------------------- + +/ struct StopWatch { @safe:// @@@BUG@@@ workaround for bug 4211 @@ -505,20 +29490,20 @@ private: bool m_FlagStarted = false; - // Ticks at the time of StopWatch starting mesurement. - Ticks m_TimeStart; + // TickDuration at the time of StopWatch starting measurement. + TickDuration m_TimeStart; - // Ticks as total time of measurement. - Ticks m_TimeMeasured; + // Total time that StopWatch ran. + TickDuration m_TimeMeasured; public: - /*************************************************************************** - * auto start with constructor - */ + /++ + auto start with constructor + +/ this(AutoStart autostart) { if (autostart) @@ -535,23 +29520,23 @@ public: } - /*************************************************************************** - * Reset the time measurement. - */ + /++ + Reset the stop watch. + +/ @safe void reset() { if (m_FlagStarted) { - // Set current systime if StopWatch is measuring. - m_TimeStart = systime(); + // Set current system time if StopWatch is measuring. + m_TimeStart = Clock.currSystemTick; } else { // Set zero if StopWatch is not measuring. - m_TimeStart.value = 0; + m_TimeStart.length = 0; } - m_TimeMeasured.value = 0; + m_TimeMeasured.length = 0; } @@ -561,20 +29546,20 @@ public: sw.start(); sw.stop(); sw.reset(); - assert(sw.peek().seconds == 0); + assert(sw.peek().to!("seconds", real) == 0); } - /*************************************************************************** - * Start the time measurement. - */ + /++ + Start the stop watch. + +/ @safe void start() { assert(!m_FlagStarted); StopWatch sw; m_FlagStarted = true; - m_TimeStart = systime(); + m_TimeStart = Clock.currSystemTick; } @@ -588,19 +29573,19 @@ public: catch (Error e) doublestart = false; assert(!doublestart); sw.stop(); - assert((t1 - sw.peek()).seconds <= 0); + assert((t1 - sw.peek()).to!("seconds", real) <= 0); } - /*************************************************************************** - * Stop the time measurement. - */ + /++ + Stop the stop watch. + +/ @safe void stop() { assert(m_FlagStarted); m_FlagStarted = false; - m_TimeMeasured += systime() - m_TimeStart; + m_TimeMeasured += Clock.currSystemTick - m_TimeStart; } @@ -614,19 +29599,20 @@ public: try sw.stop(); catch (Error e) doublestop = false; assert(!doublestop); - assert((t1 - sw.peek()).seconds == 0); + assert((t1 - sw.peek()).to!("seconds", real) == 0); } - /*************************************************************************** - * Peek Ticks of measured time. - */ - @safe const - Ticks peek() + /++ + Peek at the amount of time which has passed since the stop watch was + started. + +/ + @safe + TickDuration peek() const { if(m_FlagStarted) { - return systime() - m_TimeStart + m_TimeMeasured; + return Clock.currSystemTick - m_TimeStart + m_TimeMeasured; } return m_TimeMeasured; } @@ -646,114 +29632,44 @@ public: } -/******************************************************************************* - * Ticks of system time. - */ -@trusted -Ticks systime() -{ - version (Windows) - { - ulong ticks; - if (QueryPerformanceCounter(cast(long*)&ticks) == 0) - { - throw new Exception(sysErrorString(GetLastError())); - } - return Ticks(ticks); - } - else version (Posix) - { - static if (is(typeof(clock_gettime)==function)) - { - timespec ts; - errnoEnforce(clock_gettime(CLOCK_REALTIME, &ts) == 0, - "Failed in gettimeofday"); - return Ticks(ts.tv_sec * Ticks.ticksPerSec + - ts.tv_nsec * Ticks.ticksPerSec / 1000 / 1000 / 1000); - } - else - { - timeval tv; - errnoEnforce(gettimeofday(&tv, null) == 0, - "Failed in gettimeofday"); - return Ticks(tv.tv_sec * Ticks.ticksPerSec + - tv.tv_usec * Ticks.ticksPerSec / 1000 / 1000); - } - } -} - - -unittest -{ - auto t = systime(); - assert(t.value); -} - - -/******************************************************************************* - * Ticks when application begin running. - */ -@safe -Ticks apptime() -{ - return systime() - Ticks.appOrigin; -} - - -unittest -{ - auto a = systime(); - auto b = apptime(); - assert(a.value); - assert(a != b); -} - - -//############################################################################## -//############################################################################## -//### -//### Benchmarks -//### -//############################################################################## -//############################################################################## // workaround for bug4886 -@safe pure nothrow -size_t lengthof(aliases...)() +@safe +size_t lengthof(aliases...)() pure nothrow { return aliases.length; } -/******************************************************************************* - * Benchmarks code for speed assessment and comparison. - * - * Params: - * - * fun = aliases of callable objects (e.g. function names). Each should - * take no arguments. - * - * times = The number of times each function is to be executed. - * - * Returns: - * - * An array of $(D n) $(D uint)s. Element at slot $(D i) contains the - * number of milliseconds spent in calling the $(D i)th function $(D - * times) times. - * - * Examples: - *------------------------------------------------------------------------------ - *int a; - *void f0() { } - *void f1() { auto b = a; } - *void f2() { auto b = to!(string)(a); } - *auto r = benchmark!(f0, f1, f2)(10_000_000); - *------------------------------------------------------------------------------ - */ +/++ + Benchmarks code for speed assessment and comparison. + + Params: + + fun = aliases of callable objects (e.g. function names). Each should + take no arguments. + + times = The number of times each function is to be executed. + + Returns: + + An array of $(D n) $(D uint)s. Element at slot $(D i) contains the + number of milliseconds spent in calling the $(D i)th function $(D + times) times. + + Examples: +-------------------- +int a; +void f0() { } +void f1() { auto b = a; } +void f2() { auto b = to!(string)(a); } +auto r = benchmark!(f0, f1, f2)(10_000_000); +-------------------- + +/ @safe -Ticks[lengthof!(fun)()] benchmark(fun...)(uint times) +TickDuration[lengthof!(fun)()] benchmark(fun...)(uint times) if(areAllSafe!fun) { - Ticks[lengthof!(fun)()] result; + TickDuration[lengthof!(fun)()] result; StopWatch sw; sw.start(); foreach (i, Unused; fun) @@ -768,12 +29684,12 @@ Ticks[lengthof!(fun)()] benchmark(fun...)(uint times) return result; } -/// ditto + @system -Ticks[lengthof!(fun)()] benchmark(fun...)(uint times) +TickDuration[lengthof!(fun)()] benchmark(fun...)(uint times) if(!areAllSafe!fun) { - Ticks[lengthof!(fun)()] result; + TickDuration[lengthof!(fun)()] result; StopWatch sw; sw.start(); foreach (i, Unused; fun) @@ -799,75 +29715,76 @@ unittest } -/******************************************************************************* - * Return value of benchmark with two functions comparing. - */ +/++ + Return value of benchmark with two functions comparing. + +/ immutable struct ComparingBenchmarkResult { @safe: - private Ticks m_tmBase; - private Ticks m_tmTarget; + private TickDuration m_tmBase; + private TickDuration m_tmTarget; - /*************************************************************************** - * Evaluation value - * - * This return the evaluation value of performance as the ratio that is - * compared between BaseFunc's time and TargetFunc's time. - * If performance is high, this returns a high value. - */ - @property immutable - real point() + /++ + Evaluation value + + This returns the evaluation value of performance as the ratio that is + compared between BaseFunc's time and TargetFunc's time. + If performance is high, this returns a high value. + +/ + @property + real point() immutable pure { // @@@BUG@@@ workaround for bug 4689 - long t = m_tmTarget.value; - return m_tmBase.value / cast(real)t; + long t = m_tmTarget.length; + return m_tmBase.length / cast(real)t; } - /*************************************************************************** - * The time required of the target function - */ - @property immutable - public Ticks targetTime() + /++ + The time required of the target function + +/ + @property + public TickDuration targetTime() immutable pure { return m_tmTarget; } - /*************************************************************************** - * The time required of the base function - */ - @property immutable - public Ticks baseTime() + /++ + The time required of the base function + +/ + @property + public TickDuration baseTime() immutable pure { return m_tmBase; } } -/******************************************************************************* - * Benchmark with two functions comparing. - * - * Params: - * baseFunc = The function to become the base of the speed. - * targetFunc = The function that wants to measure speed. - * times = The number of times each function is to be executed. - * Examples: - *------------------------------------------------------------------------------ - *void f1() { - * // ... - *} - *void f2() { - * // ... - *} - * - *void main() { - * auto b = comparingBenchmark!(f1, f2, 0x80); - * writeln(b.point); - *} - *------------------------------------------------------------------------------ - */ +/++ + Benchmark with two functions comparing. + + Params: + baseFunc = The function to become the base of the speed. + targetFunc = The function that wants to measure speed. + times = The number of times each function is to be executed. + + Examples: +-------------------- +void f1() { + // ... +} +void f2() { + // ... +} + +void main() { + auto b = comparingBenchmark!(f1, f2, 0x80); + writeln(b.point); +} +-------------------- + +/ @safe ComparingBenchmarkResult comparingBenchmark( alias baseFunc, alias targetFunc, int times = 0xfff)() @@ -913,46 +29830,1189 @@ unittest } -//############################################################################## -//############################################################################## -//### -//### Helper functions -//### -//############################################################################## -//############################################################################## +//============================================================================== +// Section with other types. +//============================================================================== + +/++ + Exception type used by std.datetime. + +/ +alias TimeException DateTimeException; -version (D_Ddoc) +/++ + Effectively a namespace to make it clear where its methods are getting + their time from. It cannot be instantiated. + +/ +final class Clock { - /*************************************************************************** - * Scope base measuring time. - * - * When a value that is returned by this function is destroyed, - * func will run. - * func is unaly function that requires Ticks. - * - * Examples: - *-------------------------------------------------------------------------- - *writeln("benchmark start!"); - *{ - * auto mt = measureTime!((a){assert(a.seconds);}); - * doSomething(); - *} - *writeln("benchmark end!"); - *-------------------------------------------------------------------------- - */ +public: + + /++ + Returns the current time in the local timezone. + +/ + static SysTime currTime(immutable TimeZone tz = LocalTime()) + { + return SysTime(currStdTime, tz); + } + + unittest + { + //Verify that currTime() at least gives a time + //which isn't way off in the past (like if the + //conversions between epochs in currStdTime() was off). + assert(currTime > SysTime(Date(2010, 11, 1))); + + assert(currTime(UTC()).timezone is UTC()); + } + + + /++ + Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the + current time. + + Throws: + ErrnoException (on Posix) or Exception (on Windows) if it fails to + get the time of day. + +/ + @trusted + static @property long currStdTime() + { + enum hnsecsToUnixEpoch = 621_355_968_000_000_000L; + + version(Windows) + { + enum adjustmentFromFileTime = 116_444_736_000_000_000L; + + FILETIME fileTime; + + GetSystemTimeAsFileTime(&fileTime); + + ulong tempHNSecs = fileTime.dwHighDateTime; + tempHNSecs <<= 32; + tempHNSecs |= fileTime.dwLowDateTime; + + return cast(long)tempHNSecs - adjustmentFromFileTime + hnsecsToUnixEpoch; + } + else version(Posix) + { + static if(is(typeof(clock_gettime))) + { + timespec ts; + + if(clock_gettime(CLOCK_REALTIME, &ts) != 0) + throw new TimeException("Failed in clock_gettime()."); + + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + else + { + timeval tv; + + if(gettimeofday(&tv, null) != 0) + throw new TimeException("Failed in gettimeofday()."); + + return convert!("seconds", "hnsecs")(tv.tv_sec) + + convert!("usecs", "hnsecs")(tv.tv_usec) + + hnsecsToUnixEpoch; + } + } + } + + + /++ + The current system tick. The number of ticks per second varies from + system to system. This uses a monotonic clock, so it's intended for + precision timing by comparing relative time values, not for getting + the current system time. + + Warning: + On some systems, the monotonic clock may stop counting when + the computer goes to sleep or hibernates. So, the monotonic + clock could be off if that occurs. This is known to happen + on Mac OS X. It has not been tested whether it occurs on + either Windows or on Linux. + + Throws: + TimeException if it fails to get the time. + +/ + @safe + static @property TickDuration currSystemTick() + { + return TickDuration.currSystemTick(); + } + + unittest + { + auto t = Clock.currSystemTick; + assert(t.length > 0); + } + + + /++ + The current number of system ticks since the application started. + The number of ticks per second varies from system to system. + This uses a monotonic clock. + + Warning: + On some systems, the monotonic clock may stop counting when + the computer goes to sleep or hibernates. So, the monotonic + clock could be off if that occurs. This is known to happen + on Mac OS X. It has not been tested whether it occurs on + either Windows or on Linux. + + Throws: + ErrnoException (on Posix) or Exception (on Windows) if it fails to + get the time of day. + +/ + @safe + static @property TickDuration currAppTick() + { + return currSystemTick - TickDuration.appOrigin; + } + + unittest + { + auto a = Clock.currSystemTick; + auto b = Clock.currAppTick; + assert(a.length); + assert(a != b); + } + + + //TODO Add support for monotonic clocks. + +private: + + @disable this() {} +} + + +//============================================================================== +// Section with public helper functions and templates. +//============================================================================== + +/++ + Whether the given type defines all of the necessary functions for it to + function as a time point. + +/ +template isTimePoint(T) +{ + enum isTimePoint = hasMin!T && + hasMax!T && + hasOverloadedOpBinaryWithDuration!T && + hasOverloadedOpAssignWithDuration!T && + hasOverloadedOpBinaryWithSelf!T; +} + +unittest +{ + static assert(isTimePoint!(Date)); + static assert(isTimePoint!(DateTime)); + static assert(isTimePoint!(TimeOfDay)); + static assert(isTimePoint!(SysTime)); + static assert(isTimePoint!(const Date)); + static assert(isTimePoint!(const DateTime)); + static assert(isTimePoint!(const TimeOfDay)); + static assert(isTimePoint!(const SysTime)); + static assert(isTimePoint!(immutable Date)); + static assert(isTimePoint!(immutable DateTime)); + static assert(isTimePoint!(immutable TimeOfDay)); + static assert(isTimePoint!(immutable SysTime)); +} + + +/++ + Whether the given Gregorian Year is a leap year. + + Params: + year = The year to to be tested. + +/ +static bool yearIsLeapYear(int year) pure nothrow +{ + if(year % 400 == 0) + return true; + + if(year % 100 == 0) + return false; + + return year % 4 == 0; +} + +unittest +{ + //Test A.D. + assert(!yearIsLeapYear(1)); + assert(!yearIsLeapYear(2)); + assert(!yearIsLeapYear(3)); + assert(yearIsLeapYear(4)); + assert(!yearIsLeapYear(5)); + assert(!yearIsLeapYear(6)); + assert(!yearIsLeapYear(7)); + assert(yearIsLeapYear(8)); + + assert(!yearIsLeapYear(100)); + assert(!yearIsLeapYear(200)); + assert(!yearIsLeapYear(300)); + assert(yearIsLeapYear(400)); + assert(!yearIsLeapYear(500)); + assert(!yearIsLeapYear(600)); + assert(!yearIsLeapYear(700)); + assert(yearIsLeapYear(800)); + + assert(yearIsLeapYear(1996)); + assert(!yearIsLeapYear(1997)); + assert(!yearIsLeapYear(1998)); + assert(!yearIsLeapYear(1999)); + assert(yearIsLeapYear(2000)); + assert(!yearIsLeapYear(2001)); + assert(!yearIsLeapYear(2002)); + assert(!yearIsLeapYear(2003)); + assert(yearIsLeapYear(2004)); + assert(!yearIsLeapYear(2005)); + assert(!yearIsLeapYear(2006)); + assert(!yearIsLeapYear(2007)); + assert(yearIsLeapYear(2008)); + assert(!yearIsLeapYear(2009)); + assert(!yearIsLeapYear(2010)); + + //Test B.C. + assert(yearIsLeapYear(0)); + assert(!yearIsLeapYear(-1)); + assert(!yearIsLeapYear(-2)); + assert(!yearIsLeapYear(-3)); + assert(yearIsLeapYear(-4)); + assert(!yearIsLeapYear(-5)); + assert(!yearIsLeapYear(-6)); + assert(!yearIsLeapYear(-7)); + assert(yearIsLeapYear(-8)); + + assert(!yearIsLeapYear(-100)); + assert(!yearIsLeapYear(-200)); + assert(!yearIsLeapYear(-300)); + assert(yearIsLeapYear(-400)); + assert(!yearIsLeapYear(-500)); + assert(!yearIsLeapYear(-600)); + assert(!yearIsLeapYear(-700)); + assert(yearIsLeapYear(-800)); + + assert(yearIsLeapYear(-1996)); + assert(!yearIsLeapYear(-1997)); + assert(!yearIsLeapYear(-1998)); + assert(!yearIsLeapYear(-1999)); + assert(yearIsLeapYear(-2000)); + assert(!yearIsLeapYear(-2001)); + assert(!yearIsLeapYear(-2002)); + assert(!yearIsLeapYear(-2003)); + assert(yearIsLeapYear(-2004)); + assert(!yearIsLeapYear(-2005)); + assert(!yearIsLeapYear(-2006)); + assert(!yearIsLeapYear(-2007)); + assert(yearIsLeapYear(-2008)); + assert(!yearIsLeapYear(-2009)); + assert(!yearIsLeapYear(-2010)); +} + + +/++ + Converts a time_t (which uses midnight, January 1st, 1970 UTC as its epoch + and seconds as its units) to std time (which uses midnight, January 1st, + 1 A.D. UTC and hnsecs as its units). + + Params: + unixTime = The time_t to convert. + +/ +long unixTimeToStdTime(time_t unixTime) pure nothrow +{ + return 621355968000000000L + convert!("seconds", "hnsecs")(unixTime); +} + +unittest +{ + assertPred!"=="(unixTimeToStdTime(0), 621355968000000000L); //Midnight, January 1st, 1970 + assertPred!"=="(unixTimeToStdTime(86_400), 621355968000000000L + 864_000_000_000L); //Midnight, January 2nd, 1970 + assertPred!"=="(unixTimeToStdTime(-86_400), 621355968000000000L - 864_000_000_000L); //Midnight, December 31st, 1969 + + assertPred!"=="(unixTimeToStdTime(0), (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); + assertPred!"=="(unixTimeToStdTime(0), (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); +} + + +/++ + Converts std time (which uses midnight, January 1st, 1 A.D. UTC as its epoch + and hnsecs as its units) to time_t (which uses midnight, January 1st, 1970 UTC + as its epoch and seconds as its units). If time_t is 32 bits, rather than 64, + and the result can't fit in a 32-bit value, then the closest value that can + be held in 32 bits will be used (so time_t.max if it goes over or time_t.min + if it goes under). + + Note: + While Windows systems require that time_t be non-negative (in spite of + time_t being signed), this function still returns negative numbers on + Windows, since it's more flexible to allow negative time_t for those + who need it. So, if you're on Windows and are using the standard C + functions or Win32 API functions which take a time_t, you may want to + check whether the return value of $(D stdTimeToUnixTime()) is non-negative. + + Params: + stdTime = The std time to convert. + +/ +time_t stdTimeToUnixTime(long stdTime) pure nothrow +{ + immutable hnsecsAtUnixTimeEpoch = convert!("hnsecs", "seconds")(stdTime - 621355968000000000L); + + static if(time_t.sizeof >= long.sizeof) + return cast(time_t)hnsecsAtUnixTimeEpoch; + else + { + if(hnsecsAtUnixTimeEpoch > 0) + { + if(hnsecsAtUnixTimeEpoch > time_t.max) + return time_t.max; + return cast(time_t)hnsecsAtUnixTimeEpoch; + } + + if(hnsecsAtUnixTimeEpoch < time_t.min) + return time_t.min; + + return cast(time_t)hnsecsAtUnixTimeEpoch; + } +} + +unittest +{ + assertPred!"=="(stdTimeToUnixTime(621355968000000000L), 0); //Midnight, January 1st, 1970 + assertPred!"=="(stdTimeToUnixTime(621355968000000000L + 864_000_000_000L), 86_400); //Midnight, January 2nd, 1970 + assertPred!"=="(stdTimeToUnixTime(621355968000000000L - 864_000_000_000L), -86_400); //Midnight, December 31st, 1969 + + assertPred!"=="(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"), 0); + assertPred!"=="(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"), 0); +} + + +version(Windows) +{ + /++ + On Windows, this converts a SYSTEMTIME struct to a SysTime. + + Params: + st = The SYSTEMTIME struct to convert. + tz = The time zone that the time in the SYSTEMTIME struct + is assumed to be (if the SYSTEMTIME was supplied by a + Windows system call, the SYSTEMTIME will either be in + local time or UTC, depending on the call). + + Throws: + DateTimeException if the given SYSTEMTIME will not fit in a SysTime, + which is highly unlikely to happen given that SysTime.max is in 29,228 A.D. + and the maximum SYSTEMTIME is in 30,827 A.D. + +/ + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) pure + { + const max = SysTime.max; + + static void throwLaterThanMax() + { + throw new DateTimeException("The given SYSTEMTIME is for a date greater than SysTime.max."); + } + + if(st.wYear > max.year) + throwLaterThanMax(); + else if(st.wYear == max.year) + { + if(st.wMonth > max.month) + throwLaterThanMax(); + else if(st.wMonth == max.month) + { + if(st.wDay > max.day) + throwLaterThanMax(); + else if(st.wDay == max.day) + { + if(st.wHour > max.hour) + throwLaterThanMax(); + else if(st.wHour == max.hour) + { + if(st.wMinute > max.minute) + throwLaterThanMax(); + else if(st.wMinute == max.minute) + { + if(st.wSecond > max.second) + throwLaterThanMax(); + else if(st.wSecond == max.second) + { + if(st.wMilliseconds > max.fracSec.msecs) + throwLaterThanMax(); + } + } + } + } + } + } + + auto dt = DateTime(st.wYear, st.wMonth, st.wDay, + st.wHour, st.wMinue, st.wSecond); + + return SysTime(dt, FracSec.from!"msecs"(st.wMilliseconds), tz); + } + + unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + auto converted = SYSTEMTIMEToSystemTime(&st); + + assertPred!"<="(sysTime, converted); + assertPred!"<="(converted - sysTime, dur!"msecs"(10)); + } + + + /++ + On Windows, this converts a SysTime to a SYSTEMTIME struct. + + The SYSTEMTIME will be set using the given SysTime's time zone, so + if you want the SYSTEMTIME to be in UTC, set the SysTime's time zone + to UTC. + + Params: + sysTime = The SysTime to convert. + st = The SYSTEMTIME struct to fill in with the date and + time from sysTime. + + Throws: + DateTimeException if the given SysTime will not fit in a SYSTEMTIME. + This will only happen if the SysTime's date is prior to 1601 A.D. + +/ + void SysTimeToSYSTEMTIME(in SysTime sysTime, SYSTEMTIME* st) pure + { + immutable dt = cast(DateTime)sysTime; + + if(dt.year < 1601) + throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the year 1601."); + + st.wYear = dt.year; + st.wMonth = dt.month; + st.wDayOfWeek = dt.dayOfWeek; + st.wDay = dt.day; + st.wHour = dt.hour; + st.wMinute = dt.minute; + st.wSecond = dt.second; + st.wMilliseconds = sysTime.fracSec.msecs; + } + + unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); + + SYSTEMTIME result = void; + SysTimeToSYSTEMTIME(sysTime, &result); + + assertPred!"=="(st.wYear, result.wYear); + assertPred!"=="(st.wMonth, result.wMonth); + assertPred!"=="(st.wDayOfWeek, result.wDayOfWeek); + assertPred!"=="(st.wDay, result.wDay); + assertPred!"=="(st.wHour, result.wHour); + assertPred!"=="(st.wMinute, result.wMinute); + assertPred!"=="(st.wSecond, result.wSecond); + assertPred!"=="(st.wMillisecond, result.wMillisecond); + } + + + /++ + On Windows, this converts a FILETIME struct to a SysTime. + + Params: + ft = The FILETIME struct to convert. + tz = The time zone that the SysTime will be in (FILETIMEs are + in UTC). + + Throws: + DateTimeException if the given FILETIME will not fit in a SysTime or + if the FILETIME cannot be converted to a SYSTEMTIME. + +/ + SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) pure + { + SYSTEMTIME st = void; + + if(!FileTimeToSystemTime(ft, &st)) + throw new DateTimeException("FileTimeToSystemTime() failed."); + + auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); + sysTime.timezone = tz; + + return sysTime; + } + + unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + + auto converted = FILETIMEToSysTime(&ft); + + assertPred!"<="(sysTime, converted); + assertPred!"<="(converted - sysTime, dur!"msecs"(10)); + } + + + /++ + On Windows, this converts a SysTime to a FILETIME struct. + + Params: + sysTime = The SysTime to convert. + ft = The FILETIME struct to fill in with the date and + time from sysTime. + + Throws: + DateTimeException if the given SysTime will not fit in a FILETIME. + +/ + void SysTimeToFILETIME(SysTime sysTime, FILETIME* ft) pure + { + sysTime.timezone = UTC(); + immutable dt = cast(DateTime)sysTime; + + SYSTEMTIME st = void; + SysTimeToSYSTEMTIME(sysTime, st); + + SystemTimeToFileTime(&st, ft); + } + + unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + auto sysTime = FILETIMEToSysTime(&ft, UTC()); + + FILETIME result = void; + SysTimeToFILETIME(sysTime, &result); + + assertPred!"=="(ft.dwLowDateTime, result.fwLowDateTime); + assertPred!"=="(ft.dwHighDateTime, result.fwHighDateTime); + } +} +else version(D_Ddoc) +{ + //These types don't exist outside of Windows. + alias int SYSTEMTIME; + alias int FILETIME; + + /++ + On Windows, this converts a SYSTEMTIME struct to a SysTime. + + Params: + st = The SYSTEMTIME struct to convert. + tz = The time zone that the time in the SYSTEMTIME struct + is assumed to be (if the SYSTEMTIME was supplied by a + Windows system call, the SYSTEMTIME will either be in + local time or UTC, depending on the call). + + Throws: + DateTimeException if the given SYSTEMTIME will not fit in a SysTime, + which is highly unlikely to happen given that SysTime.max is in 29,228 A.D. + and the maximum SYSTEMTIME is in 30,827 A.D. + +/ + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) pure + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + On Windows, this converts a SysTime to a SYSTEMTIME struct. + + The SYSTEMTIME will be set using the given SysTime's time zone, so + if you want the SYSTEMTIME to be in UTC, set the SysTime's time zone + to UTC. + + Params: + sysTime = The SysTime to convert. + st = The SYSTEMTIME struct to fill in with the date and + time from sysTime. + + Throws: + DateTimeException if the given SysTime will not fit in a SYSTEMTIME. + This will only happen if the SysTime's date is prior to 1601 A.D. + +/ + void SysTimeToSYSTEMTIME(in SysTime sysTime, SYSTEMTIME* st) pure + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + On Windows, this converts a FILETIME struct to a SysTime. + + Params: + ft = The FILETIME struct to convert. + tz = The time zone that the SysTime will be in (FILETIMEs are + in UTC). + + Throws: + DateTimeException if the given FILETIME will not fit in a SysTime or + if the FILETIME cannot be converted to a SYSTEMTIME. + +/ + SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) pure + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } + + + /++ + On Windows, this converts a SysTime to a FILETIME struct. + + Params: + sysTime = The SysTime to convert. + ft = The FILETIME struct to fill in with the date and + time from sysTime. + + Throws: + DateTimeException if the given SysTime will not fit in a FILETIME. + +/ + void SysTimeToFILETIME(SysTime sysTime, FILETIME* ft) pure + { + assert(0, "No implementation. Function exists only for DDoc generation"); + } +} + + +/++ + Type representing the DOS file date/time format. + +/ +typedef uint DosFileTime; + +/++ + Converts from DOS file date/time to SysTime. + + Params: + dft = The DOS file time to convert. + tz = The time zone which the DOS file time is assumed to be in. + + Throws: + DateTimeException if the DosFileTime is invalid. + +/ +SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) +{ + uint dt = cast(uint)dft; + + if(dt == 0) + throw new DateTimeException("Invalid DosFileTime."); + + int year = ((dt >> 25) & 0x7F) + 1980; + int month = ((dt >> 21) & 0x0F) - 1; // 1..12 + int dayOfMonth = ((dt >> 16) & 0x1F); // 1..31 + int hour = (dt >> 11) & 0x1F; // 0..23 + int minute = (dt >> 5) & 0x3F; // 0..59 + int second = (dt << 1) & 0x3E; // 0..58 (in 2 second increments) + + SysTime sysTime = void; + + try + return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); + catch(DateTimeException dte) + throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); +} + + +/++ + Converts from SysTime to DOS file date/time. + + Params: + sysTime = The SysTime to convert. + + Throws: + DateTimeException if the given SysTime cannot be converted to + a DosFileTime. + +/ +DosFileTime SysTimeToDosFileTime(SysTime sysTime) +{ + auto dateTime = cast(DateTime)sysTime; + + if(dateTime.year < 1980) + throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); + + if(dateTime.year > 2107) + throw new DateTimeException("DOS File Times cannot hold dates passed 2107."); + + uint retval = 0; + retval = (dateTime.year - 1980) << 25; + retval |= ((dateTime.month + 1) & 0x0F) << 21; + retval |= (dateTime.day & 0x1F) << 16; + retval |= (dateTime.hour & 0x1F) << 11; + retval |= (dateTime.minute & 0x3F) << 5; + retval |= (dateTime.second >> 1) & 0x1F; + + return cast(DosFileTime)retval; +} + + + +/++ + Whether all of the given strings are valid units of time. + + "nsecs" is not considered a valid unit of time. Nothing in + std.datetime can handle precision greater than hnsecs, and + the few functions in core.time which deal with "nsecs" + deal with it explicitly. + +/ +bool validTimeUnits(string[] units...) +{ + foreach(str; units) + { + if(!canFind(timeStrings.dup, str)) + return false; + } + + return true; +} + + +/++ + Compares two TimeStrings. "years" are the largest units and "hnsecs" are + the smallest. + + Returns: + $(TABLE + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + + Throws: + DateTimeException if either of the given strings is not a valid time + unit string. + +/ +int cmpTimeUnits(string lhs, string rhs) +{ + auto tstrings = timeStrings.dup; + immutable indexOfLHS = std.algorithm.indexOf(tstrings, lhs); + immutable indexOfRHS = std.algorithm.indexOf(tstrings, rhs); + + enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); + enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); + + if(indexOfLHS < indexOfRHS) + return -1; + if(indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +unittest +{ + foreach(i, outerUnits; timeStrings) + { + assertPred!"=="(cmpTimeUnits(outerUnits, outerUnits), 0); + + //For some reason, $ won't compile. + foreach(innerUnits; timeStrings[i+1 .. timeStrings.length]) + assertPred!"=="(cmpTimeUnits(outerUnits, innerUnits), -1); + } + + foreach(i, outerUnits; timeStrings) + { + foreach(innerUnits; timeStrings[0 .. i]) + assertPred!"=="(cmpTimeUnits(outerUnits, innerUnits), 1); + } +} + + +/++ + Compares two time unit strings at compile time. "years" are the largest + units and "hnsecs" are the smallest. + + This template is used instead of $(D cmpTimeUnits()) because exceptions + can't be thrown at compile-time and $(D cmpTimeUnits()) must enforce that + the strings it's given are valid time unit strings. This template uses a + template constraint instead. + + Returns: + $(TABLE + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ +template CmpTimeUnits(string lhs, string rhs) + if(validTimeUnits(lhs, rhs)) +{ + enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); +} + + +/+ + Helper function for CmpTimeUnits. + +/ +private int cmpTimeUnitsCTFE(string lhs, string rhs) +{ + auto tstrings = timeStrings.dup; + immutable indexOfLHS = std.algorithm.indexOf(tstrings, lhs); + immutable indexOfRHS = std.algorithm.indexOf(tstrings, rhs); + + if(indexOfLHS < indexOfRHS) + return -1; + if(indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +unittest +{ + static string genTest(size_t index) + { + auto currUnits = timeStrings[index]; + auto test = `assertPred!"=="(CmpTimeUnits!("` ~ currUnits ~ `", "` ~ currUnits ~ `"), 0); `; + + //For some reason, $ won't compile. + foreach(units; timeStrings[index + 1 .. timeStrings.length]) + test ~= `assertPred!"=="(CmpTimeUnits!("` ~ currUnits ~ `", "` ~ units ~ `"), -1); `; + + foreach(units; timeStrings[0 .. index]) + test ~= `assertPred!"=="(CmpTimeUnits!("` ~ currUnits ~ `", "` ~ units ~ `"), 1); `; + + return test; + } + + mixin(genTest(0)); + mixin(genTest(1)); + mixin(genTest(2)); + mixin(genTest(3)); + mixin(genTest(4)); + mixin(genTest(5)); + mixin(genTest(6)); + mixin(genTest(7)); + mixin(genTest(8)); + mixin(genTest(9)); +} + + +/++ + Returns whether the given value is valid for the given unit type when in a + time point. Naturally, a duration is not held to a particular range, but + the values in a time point are (e.g. a month must be in the range of + 1 - 12 inclusive). + + Params: + units = The units of time to validate. + value = The number to validate. + + Examples: +-------------------- +assert(valid!"hours"(12)); +assert(!valid!"hours"(32)); +assert(valid!"months"(12)); +assert(!valid!"months"(13)); +-------------------- + +/ +bool valid(string units)(int value) pure nothrow + if(units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + static if(units == "months") + return value >= Month.jan && value <= Month.dec; + else static if(units == "hours") + return value >= 0 && value <= TimeOfDay.maxHour; + else static if(units == "minutes") + return value >= 0 && value <= TimeOfDay.maxMinute; + else static if(units == "seconds") + return value >= 0 && value <= TimeOfDay.maxSecond; +} + +unittest +{ + //Verify Examples. + assert(valid!"hours"(12)); + assert(!valid!"hours"(32)); + assert(valid!"months"(12)); + assert(!valid!"months"(13)); +} + + +/++ + Returns whether the given day is valid for the given year and month. + + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + +/ +bool valid(string units)(int year, int month, int day) pure nothrow + if(units == "days") +{ + return day > 0 && day <= maxDay(year, month); +} + + +/++ + Params: + units = The units of time to validate. + value = The number to validate. + file = The file that the DateTimeException will list if thrown. + line = The line number that the DateTimeException will list if thrown. + + Throws: + DateTimeException if valid!units(value) is false. + +/ +void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) pure + if(units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + static if(units == "months") + { + if(!valid!units(value)) + throw new DateTimeException(numToString(value) ~ " is not a valid month of the year.", file, line); + } + else static if(units == "hours") + { + if(!valid!units(value)) + throw new DateTimeException(numToString(value) ~ " is not a valid hour of the day.", file, line); + } + else static if(units == "minutes") + { + if(!valid!units(value)) + throw new DateTimeException(numToString(value) ~ " is not a valid minute of an hour.", file, line); + } + else static if(units == "seconds") + { + if(!valid!units(value)) + throw new DateTimeException(numToString(value) ~ " is not a valid second of a minute.", file, line); + } +} + + +/++ + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + file = The file that the DateTimeException will list if thrown. + line = The line number that the DateTimeException will list if thrown. + + Throws: + DateTimeException if valid!"days"(year, month, day) is false. + +/ +void enforceValid(string units)(int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) pure + if(units == "days") +{ + if(!valid!"days"(year, month, day)) + { + throw new DateTimeException(numToString(day) ~ + " is not a valid day in " ~ + monthToString(month) ~ + " in " ~ + numToString(year), file, line); + } +} + + +/++ + Returns the number of months from the current months of the year to the + given month of the year. If they are the same, then the result is 0. + + Params: + currMonth = The current month of the year. + month = The month of the year to get the number of months to. + +/ +static int monthsToMonth(int currMonth, int month) pure +{ + enforceValid!"months"(currMonth); + enforceValid!"months"(month); + + if(currMonth == month) + return 0; + + if(currMonth < month) + return month - currMonth; + + return (Month.dec - currMonth) + month; +} + +unittest +{ + assertPred!"=="(monthsToMonth(Month.jan, Month.jan), 0); + assertPred!"=="(monthsToMonth(Month.jan, Month.feb), 1); + assertPred!"=="(monthsToMonth(Month.jan, Month.mar), 2); + assertPred!"=="(monthsToMonth(Month.jan, Month.apr), 3); + assertPred!"=="(monthsToMonth(Month.jan, Month.may), 4); + assertPred!"=="(monthsToMonth(Month.jan, Month.jun), 5); + assertPred!"=="(monthsToMonth(Month.jan, Month.jul), 6); + assertPred!"=="(monthsToMonth(Month.jan, Month.aug), 7); + assertPred!"=="(monthsToMonth(Month.jan, Month.sep), 8); + assertPred!"=="(monthsToMonth(Month.jan, Month.oct), 9); + assertPred!"=="(monthsToMonth(Month.jan, Month.nov), 10); + assertPred!"=="(monthsToMonth(Month.jan, Month.dec), 11); + + assertPred!"=="(monthsToMonth(Month.may, Month.jan), 8); + assertPred!"=="(monthsToMonth(Month.may, Month.feb), 9); + assertPred!"=="(monthsToMonth(Month.may, Month.mar), 10); + assertPred!"=="(monthsToMonth(Month.may, Month.apr), 11); + assertPred!"=="(monthsToMonth(Month.may, Month.may), 0); + assertPred!"=="(monthsToMonth(Month.may, Month.jun), 1); + assertPred!"=="(monthsToMonth(Month.may, Month.jul), 2); + assertPred!"=="(monthsToMonth(Month.may, Month.aug), 3); + assertPred!"=="(monthsToMonth(Month.may, Month.sep), 4); + assertPred!"=="(monthsToMonth(Month.may, Month.oct), 5); + assertPred!"=="(monthsToMonth(Month.may, Month.nov), 6); + assertPred!"=="(monthsToMonth(Month.may, Month.dec), 7); + + assertPred!"=="(monthsToMonth(Month.oct, Month.jan), 3); + assertPred!"=="(monthsToMonth(Month.oct, Month.feb), 4); + assertPred!"=="(monthsToMonth(Month.oct, Month.mar), 5); + assertPred!"=="(monthsToMonth(Month.oct, Month.apr), 6); + assertPred!"=="(monthsToMonth(Month.oct, Month.may), 7); + assertPred!"=="(monthsToMonth(Month.oct, Month.jun), 8); + assertPred!"=="(monthsToMonth(Month.oct, Month.jul), 9); + assertPred!"=="(monthsToMonth(Month.oct, Month.aug), 10); + assertPred!"=="(monthsToMonth(Month.oct, Month.sep), 11); + assertPred!"=="(monthsToMonth(Month.oct, Month.oct), 0); + assertPred!"=="(monthsToMonth(Month.oct, Month.nov), 1); + assertPred!"=="(monthsToMonth(Month.oct, Month.dec), 2); + + assertPred!"=="(monthsToMonth(Month.dec, Month.jan), 1); + assertPred!"=="(monthsToMonth(Month.dec, Month.feb), 2); + assertPred!"=="(monthsToMonth(Month.dec, Month.mar), 3); + assertPred!"=="(monthsToMonth(Month.dec, Month.apr), 4); + assertPred!"=="(monthsToMonth(Month.dec, Month.may), 5); + assertPred!"=="(monthsToMonth(Month.dec, Month.jun), 6); + assertPred!"=="(monthsToMonth(Month.dec, Month.jul), 7); + assertPred!"=="(monthsToMonth(Month.dec, Month.aug), 8); + assertPred!"=="(monthsToMonth(Month.dec, Month.sep), 9); + assertPred!"=="(monthsToMonth(Month.dec, Month.oct), 10); + assertPred!"=="(monthsToMonth(Month.dec, Month.nov), 11); + assertPred!"=="(monthsToMonth(Month.dec, Month.dec), 0); +} + + +/++ + Returns the number of days from the current day of the week to the given + day of the week. If they are the same, then the result is 0. + + Params: + currDoW = The current day of the week. + dow = The day of the week to get the number of days to. + +/ +static int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) pure nothrow +{ + if(currDoW == dow) + return 0; + + if(currDoW < dow) + return dow - currDoW; + + return (DayOfWeek.sat - currDoW) + dow + 1; +} + +unittest +{ + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun), 0); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon), 1); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue), 2); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed), 3); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu), 4); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri), 5); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat), 6); + + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun), 6); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon), 0); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue), 1); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed), 2); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu), 3); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri), 4); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat), 5); + + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun), 5); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon), 6); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue), 0); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed), 1); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu), 2); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri), 3); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat), 4); + + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun), 4); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon), 5); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue), 6); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed), 0); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu), 1); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri), 2); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat), 3); + + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun), 3); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon), 4); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue), 5); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed), 6); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu), 0); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri), 1); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat), 2); + + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun), 2); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon), 3); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue), 4); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed), 5); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu), 6); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri), 0); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat), 1); + + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun), 1); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon), 2); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue), 3); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed), 4); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu), 5); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri), 6); + assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat), 0); +} + + +//This should be removed as soon the bug with auto return functions not +//showing up in the documentation generated by ddoc is fixed. +version(D_Ddoc) +{ + //Here purely to make it so that TemporaryValue is defined and will compile. + alias long TemporaryValue; + + /++ + Function for starting to a stop watch time when the function is called + and stopping it when its return value goes out of scope and is destroyed. + + When the value that is returned by this function is destroyed, + $(D func()) will run. $(D func()) is a unary function that takes a + TickDuration. + + Examples: +-------------------- +writeln("benchmark start!"); +{ +auto mt = measureTime!((a){assert(a.seconds);}); +doSomething(); +} +writeln("benchmark end!"); +-------------------- + +/ TemporaryValue measureTime(alias func)(); - - - // for -unittest -D - alias void* TemporaryValue; } else { @safe { auto measureTime(alias func)() - if (isSafe!func) + if(isSafe!func) { struct TMP { @@ -969,7 +31029,7 @@ else return TMP(autoStart); } } - + @system { auto measureTime(alias func)() @@ -992,12 +31052,11 @@ else } } - @system unittest { { - auto mt = measureTime!((a){assert(a.seconds <>= 0);}); + auto mt = measureTime!((a){assert(a.to!("seconds", real) <>= 0);}); } /+ @@ -1008,3 +31067,1763 @@ unittest } +/ } + + +//============================================================================== +// Private Section. +// +// Note that the templates in the private section are documented but do _not_ +// have both +'s to begin their documentation comment blocks. This is because +// of bug 2775 ( http://d.puremagic.com/issues/show_bug.cgi?id=2775 ) which +// makes it so that all templates are actually public. Ideally, most (if not all) +// of these templates would be inside of isTimePoint, but bug 4675 +// ( http://d.puremagic.com/issues/show_bug.cgi?id=4675 ) which currently prevents +// putting more that the enum with the template's name inside of an eponymous +// template. +//============================================================================== +private: + + +//============================================================================== +// Section with private enums and constants. +//============================================================================== + +enum daysInYear = 365; /// The number of days in a non-leap year. +enum daysInLeapYear = 366; /// The numbef or days in a leap year. +enum daysIn4Years = daysInYear * 3 + daysInLeapYear; /// Number of days in 4 years. +enum daysIn100Years = daysIn4Years * 25 - 1; /// The number of days in 100 years. +enum daysIn400Years = daysIn100Years * 4 + 1; /// The number of days in 400 years. + + +//============================================================================== +// Section with private helper functions and templates. +//============================================================================== + +/+ + Template to help with converting between time units. + +/ +template hnsecsPer(string units) + if(CmpTimeUnits!(units, "months") < 0) +{ + static if(units == "hnsecs") + enum hnsecsPer = 1L; + else static if(units == "usecs") + enum hnsecsPer = 10L; + else static if(units == "msecs") + enum hnsecsPer = 1000 * hnsecsPer!"usecs"; + else static if(units == "seconds") + enum hnsecsPer = 1000 * hnsecsPer!"msecs"; + else static if(units == "minutes") + enum hnsecsPer = 60 * hnsecsPer!"seconds"; + else static if(units == "hours") + enum hnsecsPer = 60 * hnsecsPer!"minutes"; + else static if(units == "days") + enum hnsecsPer = 24 * hnsecsPer!"hours"; + else static if(units == "weeks") + enum hnsecsPer = 7 * hnsecsPer!"days"; +} + + +/+ + Splits out a particular unit from hnsecs and gives you the value for that + unit and the remaining hnsecs. It really shouldn't be used unless unless + all units larger than the given units have already been split out. + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left + after splitting out the given units. + + Returns: + The number of the given units from converting hnsecs to those units. + + Examples: +-------------------- +auto hnsecs = 2595000000007L; +immutable days = splitUnitsFromHNSecs!"days"(hnsecs); +assert(days == 3); +assert(hnsecs == 3000000007); + +immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); +assert(minutes == 5); +assert(hnsecs == 7); +-------------------- + +/ +long splitUnitsFromHNSecs(string units)(ref long hnsecs) pure nothrow + if(validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + immutable value = convert!("hnsecs", units)(hnsecs); + hnsecs -= convert!(units, "hnsecs")(value); + + return value; +} + +unittest +{ + //Verify Example. + auto hnsecs = 2595000000007L; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 3000000007); + + immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + assert(minutes == 5); + assert(hnsecs == 7); +} + + +/+ + This function is used to split out the units without getting the remaining + hnsecs. + + See_Also: + splitUnitsFromHNSecs() + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. + + Returns: + The split out value. + + Examples: +-------------------- +auto hnsecs = 2595000000007L; +immutable days = getUnitsFromHNSecs!"days"(hnsecs); +assert(days == 3); +assert(hnsecs == 2595000000007L); +-------------------- + +/ +long getUnitsFromHNSecs(string units)(long hnsecs) pure nothrow + if(validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + return convert!("hnsecs", units)(hnsecs); +} + +unittest +{ + //Verify Example. + auto hnsecs = 2595000000007L; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 2595000000007L); +} + + +/+ + This function is used to split out the units without getting the units but + just the remaining hnsecs. + + See_Also: + splitUnitsFromHNSecs() + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. + + Returns: + The remaining hnsecs. + + Examples: +-------------------- +auto hnsecs = 2595000000007L; +auto returned = removeUnitsFromHNSecs!"days"(hnsecs); +assert(returned == 3000000007); +assert(hnsecs == 2595000000007L); +-------------------- + +/ +long removeUnitsFromHNSecs(string units)(long hnsecs) pure nothrow + if(validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + immutable value = convert!("hnsecs", units)(hnsecs); + + return hnsecs - convert!(units, "hnsecs")(value); +} + +unittest +{ + //Verify Example. + auto hnsecs = 2595000000007L; + auto returned = removeUnitsFromHNSecs!"days"(hnsecs); + assert(returned == 3000000007); + assert(hnsecs == 2595000000007L); +} + + +/+ + The maximum valid Day in the given month in the given year. + + Params: + year = The year to get the day for. + month = The month of the Gregorian Calendar to get the day for. + +/ +static ubyte maxDay(int year, int month) pure nothrow +in +{ + assert(valid!"months"(month)); +} +body +{ + switch(month) + { + case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: + return 31; + case Month.feb: + return yearIsLeapYear(year) ? 29 : 28; + case Month.apr, Month.jun, Month.sep, Month.nov: + return 30; + default: + assert(0, "Invalid month."); + } +} + +unittest +{ + //Test A.D. + assertPred!"=="(maxDay(1999, 1), 31); + assertPred!"=="(maxDay(1999, 2), 28); + assertPred!"=="(maxDay(1999, 3), 31); + assertPred!"=="(maxDay(1999, 4), 30); + assertPred!"=="(maxDay(1999, 5), 31); + assertPred!"=="(maxDay(1999, 6), 30); + assertPred!"=="(maxDay(1999, 7), 31); + assertPred!"=="(maxDay(1999, 8), 31); + assertPred!"=="(maxDay(1999, 9), 30); + assertPred!"=="(maxDay(1999, 10), 31); + assertPred!"=="(maxDay(1999, 11), 30); + assertPred!"=="(maxDay(1999, 12), 31); + + assertPred!"=="(maxDay(2000, 1), 31); + assertPred!"=="(maxDay(2000, 2), 29); + assertPred!"=="(maxDay(2000, 3), 31); + assertPred!"=="(maxDay(2000, 4), 30); + assertPred!"=="(maxDay(2000, 5), 31); + assertPred!"=="(maxDay(2000, 6), 30); + assertPred!"=="(maxDay(2000, 7), 31); + assertPred!"=="(maxDay(2000, 8), 31); + assertPred!"=="(maxDay(2000, 9), 30); + assertPred!"=="(maxDay(2000, 10), 31); + assertPred!"=="(maxDay(2000, 11), 30); + assertPred!"=="(maxDay(2000, 12), 31); + + //Test B.C. + assertPred!"=="(maxDay(-1999, 1), 31); + assertPred!"=="(maxDay(-1999, 2), 28); + assertPred!"=="(maxDay(-1999, 3), 31); + assertPred!"=="(maxDay(-1999, 4), 30); + assertPred!"=="(maxDay(-1999, 5), 31); + assertPred!"=="(maxDay(-1999, 6), 30); + assertPred!"=="(maxDay(-1999, 7), 31); + assertPred!"=="(maxDay(-1999, 8), 31); + assertPred!"=="(maxDay(-1999, 9), 30); + assertPred!"=="(maxDay(-1999, 10), 31); + assertPred!"=="(maxDay(-1999, 11), 30); + assertPred!"=="(maxDay(-1999, 12), 31); + + assertPred!"=="(maxDay(-2000, 1), 31); + assertPred!"=="(maxDay(-2000, 2), 29); + assertPred!"=="(maxDay(-2000, 3), 31); + assertPred!"=="(maxDay(-2000, 4), 30); + assertPred!"=="(maxDay(-2000, 5), 31); + assertPred!"=="(maxDay(-2000, 6), 30); + assertPred!"=="(maxDay(-2000, 7), 31); + assertPred!"=="(maxDay(-2000, 8), 31); + assertPred!"=="(maxDay(-2000, 9), 30); + assertPred!"=="(maxDay(-2000, 10), 31); + assertPred!"=="(maxDay(-2000, 11), 30); + assertPred!"=="(maxDay(-2000, 12), 31); +} + + +/+ + Returns the day of the week for the given day of the Gregorian Calendar. + + Params: + day = The day of the Gregorian Calendar for which to get the day of + the week. + +/ +DayOfWeek getDayOfWeek(int day) pure nothrow +{ + //January 1st, 1 A.D. was a Monday + if(day >= 0) + return cast(DayOfWeek)(day % 7); + else + { + immutable dow = cast(DayOfWeek)((day % 7) + 7); + + if(dow == 7) + return DayOfWeek.sun; + else + return dow; + } +} + +unittest +{ + //Test A.D. + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal), DayOfWeek.mon); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal), DayOfWeek.tue); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal), DayOfWeek.wed); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal), DayOfWeek.thu); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal), DayOfWeek.fri); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal), DayOfWeek.sat); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal), DayOfWeek.sun); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal), DayOfWeek.mon); + assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal), DayOfWeek.tue); + assertPred!"=="(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal), DayOfWeek.tue); + assertPred!"=="(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal), DayOfWeek.wed); + assertPred!"=="(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal), DayOfWeek.thu); + assertPred!"=="(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal), DayOfWeek.sat); + assertPred!"=="(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal), DayOfWeek.sat); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal), DayOfWeek.sun); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal), DayOfWeek.mon); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal), DayOfWeek.tue); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal), DayOfWeek.wed); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal), DayOfWeek.thu); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal), DayOfWeek.fri); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal), DayOfWeek.sat); + assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal), DayOfWeek.sun); + + //Test B.C. + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal), DayOfWeek.sun); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal), DayOfWeek.sat); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal), DayOfWeek.fri); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal), DayOfWeek.thu); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal), DayOfWeek.wed); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal), DayOfWeek.tue); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal), DayOfWeek.mon); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal), DayOfWeek.sun); + assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal), DayOfWeek.sat); +} + + +/++ + Returns the string representation of the given month. + + Params: + useLongName = Whether the long or short version of the month name should + be used. + plural = Whether the string should be plural or not. + + Throws: + DateTimeException if the given month is not a valid month. + +/ +string monthToString(Month month, bool useLongName = true) pure +{ + if(useLongName == true) + { + switch(month) + { + case Month.jan: + return "January"; + case Month.feb: + return "February"; + case Month.mar: + return "March"; + case Month.apr: + return "April"; + case Month.may: + return "May"; + case Month.jun: + return "June"; + case Month.jul: + return "July"; + case Month.aug: + return "August"; + case Month.sep: + return "September"; + case Month.oct: + return "October"; + case Month.nov: + return "November"; + case Month.dec: + return "December"; + default: + throw new DateTimeException("Invalid month: " ~ numToString(month)); + } + } + else + { + switch(month) + { + case Month.jan: + return "Jan"; + case Month.feb: + return "Feb"; + case Month.mar: + return "Mar"; + case Month.apr: + return "Apr"; + case Month.may: + return "May"; + case Month.jun: + return "Jun"; + case Month.jul: + return "Jul"; + case Month.aug: + return "Aug"; + case Month.sep: + return "Sep"; + case Month.oct: + return "Oct"; + case Month.nov: + return "Nov"; + case Month.dec: + return "Dec"; + default: + throw new DateTimeException("Invalid month: " ~ numToString(month)); + } + } +} + +unittest +{ + static void testMTSInvalid(Month month, bool useLongName) + { + monthToString(month, useLongName); + } + + assertThrown!DateTimeException(testMTSInvalid(cast(Month)0, true)); + assertThrown!DateTimeException(testMTSInvalid(cast(Month)0, false)); + assertThrown!DateTimeException(testMTSInvalid(cast(Month)13, true)); + assertThrown!DateTimeException(testMTSInvalid(cast(Month)13, false)); + + assertPred!"=="(monthToString(Month.jan), "January"); + assertPred!"=="(monthToString(Month.feb), "February"); + assertPred!"=="(monthToString(Month.mar), "March"); + assertPred!"=="(monthToString(Month.apr), "April"); + assertPred!"=="(monthToString(Month.may), "May"); + assertPred!"=="(monthToString(Month.jun), "June"); + assertPred!"=="(monthToString(Month.jul), "July"); + assertPred!"=="(monthToString(Month.aug), "August"); + assertPred!"=="(monthToString(Month.sep), "September"); + assertPred!"=="(monthToString(Month.oct), "October"); + assertPred!"=="(monthToString(Month.nov), "November"); + assertPred!"=="(monthToString(Month.dec), "December"); + + assertPred!"=="(monthToString(Month.jan, false), "Jan"); + assertPred!"=="(monthToString(Month.feb, false), "Feb"); + assertPred!"=="(monthToString(Month.mar, false), "Mar"); + assertPred!"=="(monthToString(Month.apr, false), "Apr"); + assertPred!"=="(monthToString(Month.may, false), "May"); + assertPred!"=="(monthToString(Month.jun, false), "Jun"); + assertPred!"=="(monthToString(Month.jul, false), "Jul"); + assertPred!"=="(monthToString(Month.aug, false), "Aug"); + assertPred!"=="(monthToString(Month.sep, false), "Sep"); + assertPred!"=="(monthToString(Month.oct, false), "Oct"); + assertPred!"=="(monthToString(Month.nov, false), "Nov"); + assertPred!"=="(monthToString(Month.dec, false), "Dec"); +} + + +/++ + Returns the Month corresponding to the given string. Casing is ignored. + + Params: + monthStr = The string representation of the month to get the Month for. + + Throws: + DateTimeException if the given month is not a valid month string. + +/ +Month monthFromString(string monthStr) +{ + switch(tolower(monthStr)) + { + case "january": + case "jan": + return Month.jan; + case "february": + case "feb": + return Month.feb; + case "march": + case "mar": + return Month.mar; + case "april": + case "apr": + return Month.apr; + case "may": + return Month.may; + case "june": + case "jun": + return Month.jun; + case "july": + case "jul": + return Month.jul; + case "august": + case "aug": + return Month.aug; + case "september": + case "sep": + return Month.sep; + case "october": + case "oct": + return Month.oct; + case "november": + case "nov": + return Month.nov; + case "december": + case "dec": + return Month.dec; + default: + throw new DateTimeException(format("Invalid month %s", monthStr)); + } +} + +unittest +{ + static void testMFSInvalid(string monthStr) + { + monthFromString(monthStr); + } + + assertThrown!DateTimeException(testMFSInvalid("Ja")); + assertThrown!DateTimeException(testMFSInvalid("Janu")); + assertThrown!DateTimeException(testMFSInvalid("Januar")); + assertThrown!DateTimeException(testMFSInvalid("Januarys")); + assertThrown!DateTimeException(testMFSInvalid("JJanuary")); + + assertPred!"=="(monthFromString(monthToString(Month.jan)), Month.jan); + assertPred!"=="(monthFromString(monthToString(Month.feb)), Month.feb); + assertPred!"=="(monthFromString(monthToString(Month.mar)), Month.mar); + assertPred!"=="(monthFromString(monthToString(Month.apr)), Month.apr); + assertPred!"=="(monthFromString(monthToString(Month.may)), Month.may); + assertPred!"=="(monthFromString(monthToString(Month.jun)), Month.jun); + assertPred!"=="(monthFromString(monthToString(Month.jul)), Month.jul); + assertPred!"=="(monthFromString(monthToString(Month.aug)), Month.aug); + assertPred!"=="(monthFromString(monthToString(Month.sep)), Month.sep); + assertPred!"=="(monthFromString(monthToString(Month.oct)), Month.oct); + assertPred!"=="(monthFromString(monthToString(Month.nov)), Month.nov); + assertPred!"=="(monthFromString(monthToString(Month.dec)), Month.dec); + + assertPred!"=="(monthFromString(monthToString(Month.jan, false)), Month.jan); + assertPred!"=="(monthFromString(monthToString(Month.feb, false)), Month.feb); + assertPred!"=="(monthFromString(monthToString(Month.mar, false)), Month.mar); + assertPred!"=="(monthFromString(monthToString(Month.apr, false)), Month.apr); + assertPred!"=="(monthFromString(monthToString(Month.may, false)), Month.may); + assertPred!"=="(monthFromString(monthToString(Month.jun, false)), Month.jun); + assertPred!"=="(monthFromString(monthToString(Month.jul, false)), Month.jul); + assertPred!"=="(monthFromString(monthToString(Month.aug, false)), Month.aug); + assertPred!"=="(monthFromString(monthToString(Month.sep, false)), Month.sep); + assertPred!"=="(monthFromString(monthToString(Month.oct, false)), Month.oct); + assertPred!"=="(monthFromString(monthToString(Month.nov, false)), Month.nov); + assertPred!"=="(monthFromString(monthToString(Month.dec, false)), Month.dec); + + assertPred!"=="(monthFromString("JANUARY"), Month.jan); + assertPred!"=="(monthFromString("JAN"), Month.jan); + assertPred!"=="(monthFromString("january"), Month.jan); + assertPred!"=="(monthFromString("jan"), Month.jan); + assertPred!"=="(monthFromString("jaNuary"), Month.jan); + assertPred!"=="(monthFromString("jaN"), Month.jan); + assertPred!"=="(monthFromString("jaNuaRy"), Month.jan); + assertPred!"=="(monthFromString("jAn"), Month.jan); +} + + +/+ + The time units which are one step smaller than the given units. + + Examples: +-------------------- +assert(nextSmallerTimeUnits!"years" == "months"); +assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); +-------------------- + +/ +template nextSmallerTimeUnits(string units) + if(validTimeUnits(units) && + timeStrings.front != units) +{ + enum nextSmallerTimeUnits = timeStrings[std.algorithm.indexOf(timeStrings.dup, units) - 1]; +} + +unittest +{ + assertPred!"=="(nextSmallerTimeUnits!"months", "weeks"); + assertPred!"=="(nextSmallerTimeUnits!"weeks", "days"); + assertPred!"=="(nextSmallerTimeUnits!"days", "hours"); + assertPred!"=="(nextSmallerTimeUnits!"hours", "minutes"); + assertPred!"=="(nextSmallerTimeUnits!"minutes", "seconds"); + assertPred!"=="(nextSmallerTimeUnits!"seconds", "msecs"); + assertPred!"=="(nextSmallerTimeUnits!"msecs", "usecs"); + + static assert(!__traits(compiles, nextSmallerTimeUnits!"hnsecs")); + + //Verify Examples + assert(nextSmallerTimeUnits!"years" == "months"); + assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); +} + + +/+ + The time units which are one step larger than the given units. + + Examples: +-------------------- +assert(nextLargerTimeUnits!"months" == "years"); +assert(nextLargerTimeUnits!"hnsecs" == "usecs"); +-------------------- + +/ +template nextLargerTimeUnits(string units) + if(validTimeUnits(units) && + timeStrings.back != units) +{ + enum nextLargerTimeUnits = timeStrings[std.algorithm.indexOf(timeStrings.dup, units) + 1]; +} + +unittest +{ + assertPred!"=="(nextLargerTimeUnits!"usecs", "msecs"); + assertPred!"=="(nextLargerTimeUnits!"msecs", "seconds"); + assertPred!"=="(nextLargerTimeUnits!"seconds", "minutes"); + assertPred!"=="(nextLargerTimeUnits!"minutes", "hours"); + assertPred!"=="(nextLargerTimeUnits!"hours", "days"); + assertPred!"=="(nextLargerTimeUnits!"days", "weeks"); + assertPred!"=="(nextLargerTimeUnits!"weeks", "months"); + + static assert(!__traits(compiles, nextLargerTimeUnits!"years")); + + //Verify Examples + assert(nextLargerTimeUnits!"months" == "years"); + assert(nextLargerTimeUnits!"hnsecs" == "usecs"); +} + + +/++ + Returns the given hnsecs as an ISO string of fractional seconds. + +/ +static string fracSecToISOString(int hnsecs) nothrow +in +{ + assert(hnsecs >= 0); +} +body +{ + try + { + string isoString = format(".%07d", hnsecs); + + while(isoString.endsWith("0")) + isoString.popBack(); + + if(isoString.length == 1) + return ""; + + return isoString; + } + catch(Exception e) + assert(0, "format() threw."); +} + +unittest +{ + assertPred!"=="(fracSecToISOString(0), ""); + assertPred!"=="(fracSecToISOString(1), ".0000001"); + assertPred!"=="(fracSecToISOString(10), ".000001"); + assertPred!"=="(fracSecToISOString(100), ".00001"); + assertPred!"=="(fracSecToISOString(1000), ".0001"); + assertPred!"=="(fracSecToISOString(10_000), ".001"); + assertPred!"=="(fracSecToISOString(100_000), ".01"); + assertPred!"=="(fracSecToISOString(1_000_000), ".1"); + assertPred!"=="(fracSecToISOString(1_000_001), ".1000001"); + assertPred!"=="(fracSecToISOString(1_001_001), ".1001001"); + assertPred!"=="(fracSecToISOString(1_071_601), ".1071601"); + assertPred!"=="(fracSecToISOString(1_271_641), ".1271641"); + assertPred!"=="(fracSecToISOString(9_999_999), ".9999999"); + assertPred!"=="(fracSecToISOString(9_999_990), ".999999"); + assertPred!"=="(fracSecToISOString(9_999_900), ".99999"); + assertPred!"=="(fracSecToISOString(9_999_000), ".9999"); + assertPred!"=="(fracSecToISOString(9_990_000), ".999"); + assertPred!"=="(fracSecToISOString(9_900_000), ".99"); + assertPred!"=="(fracSecToISOString(9_000_000), ".9"); + assertPred!"=="(fracSecToISOString(999), ".0000999"); + assertPred!"=="(fracSecToISOString(9990), ".000999"); + assertPred!"=="(fracSecToISOString(99_900), ".00999"); + assertPred!"=="(fracSecToISOString(999_000), ".0999"); +} + + +/++ + Returns a FracSec corresponding to to the given ISO string of + fractional seconds. + +/ +static FracSec fracSecFromISOString(S)(in S isoString) + if(isSomeString!S) +{ + if(isoString.empty) + return FracSec.from!"hnsecs"(0); + + auto dstr = to!dstring(isoString); + + enforce(dstr.startsWith("."), new DateTimeException("Invalid ISO String")); + dstr.popFront(); + + enforce(!dstr.empty && dstr.length <= 7, new DateTimeException("Invalid ISO String")); + enforce(!canFind!((dchar c){return !isdigit(c);})(dstr), new DateTimeException("Invalid ISO String")); + + dchar[7] fullISOString; + + foreach(i, ref dchar c; fullISOString) + { + if(i < dstr.length) + c = dstr[i]; + else + c = '0'; + } + + return FracSec.from!"hnsecs"(to!int(fullISOString[])); +} + +unittest +{ + static void testFSInvalid(string isoString) + { + fracSecFromISOString(isoString); + } + + assertThrown!DateTimeException(testFSInvalid(".")); + assertThrown!DateTimeException(testFSInvalid("0.")); + assertThrown!DateTimeException(testFSInvalid("0")); + assertThrown!DateTimeException(testFSInvalid("0000000")); + assertThrown!DateTimeException(testFSInvalid(".00000000")); + assertThrown!DateTimeException(testFSInvalid(".00000001")); + assertThrown!DateTimeException(testFSInvalid("T")); + assertThrown!DateTimeException(testFSInvalid("T.")); + assertThrown!DateTimeException(testFSInvalid(".T")); + + assertPred!"=="(fracSecFromISOString(""), FracSec.from!"hnsecs"(0)); + assertPred!"=="(fracSecFromISOString(".0000001"), FracSec.from!"hnsecs"(1)); + assertPred!"=="(fracSecFromISOString(".000001"), FracSec.from!"hnsecs"(10)); + assertPred!"=="(fracSecFromISOString(".00001"), FracSec.from!"hnsecs"(100)); + assertPred!"=="(fracSecFromISOString(".0001"), FracSec.from!"hnsecs"(1000)); + assertPred!"=="(fracSecFromISOString(".001"), FracSec.from!"hnsecs"(10_000)); + assertPred!"=="(fracSecFromISOString(".01"), FracSec.from!"hnsecs"(100_000)); + assertPred!"=="(fracSecFromISOString(".1"), FracSec.from!"hnsecs"(1_000_000)); + assertPred!"=="(fracSecFromISOString(".1000001"), FracSec.from!"hnsecs"(1_000_001)); + assertPred!"=="(fracSecFromISOString(".1001001"), FracSec.from!"hnsecs"(1_001_001)); + assertPred!"=="(fracSecFromISOString(".1071601"), FracSec.from!"hnsecs"(1_071_601)); + assertPred!"=="(fracSecFromISOString(".1271641"), FracSec.from!"hnsecs"(1_271_641)); + assertPred!"=="(fracSecFromISOString(".9999999"), FracSec.from!"hnsecs"(9_999_999)); + assertPred!"=="(fracSecFromISOString(".9999990"), FracSec.from!"hnsecs"(9_999_990)); + assertPred!"=="(fracSecFromISOString(".999999"), FracSec.from!"hnsecs"(9_999_990)); + assertPred!"=="(fracSecFromISOString(".9999900"), FracSec.from!"hnsecs"(9_999_900)); + assertPred!"=="(fracSecFromISOString(".99999"), FracSec.from!"hnsecs"(9_999_900)); + assertPred!"=="(fracSecFromISOString(".9999000"), FracSec.from!"hnsecs"(9_999_000)); + assertPred!"=="(fracSecFromISOString(".9999"), FracSec.from!"hnsecs"(9_999_000)); + assertPred!"=="(fracSecFromISOString(".9990000"), FracSec.from!"hnsecs"(9_990_000)); + assertPred!"=="(fracSecFromISOString(".999"), FracSec.from!"hnsecs"(9_990_000)); + assertPred!"=="(fracSecFromISOString(".9900000"), FracSec.from!"hnsecs"(9_900_000)); + assertPred!"=="(fracSecFromISOString(".9900"), FracSec.from!"hnsecs"(9_900_000)); + assertPred!"=="(fracSecFromISOString(".99"), FracSec.from!"hnsecs"(9_900_000)); + assertPred!"=="(fracSecFromISOString(".9000000"), FracSec.from!"hnsecs"(9_000_000)); + assertPred!"=="(fracSecFromISOString(".9"), FracSec.from!"hnsecs"(9_000_000)); + assertPred!"=="(fracSecFromISOString(".0000999"), FracSec.from!"hnsecs"(999)); + assertPred!"=="(fracSecFromISOString(".0009990"), FracSec.from!"hnsecs"(9990)); + assertPred!"=="(fracSecFromISOString(".000999"), FracSec.from!"hnsecs"(9990)); + assertPred!"=="(fracSecFromISOString(".0099900"), FracSec.from!"hnsecs"(99_900)); + assertPred!"=="(fracSecFromISOString(".00999"), FracSec.from!"hnsecs"(99_900)); + assertPred!"=="(fracSecFromISOString(".0999000"), FracSec.from!"hnsecs"(999_000)); + assertPred!"=="(fracSecFromISOString(".0999"), FracSec.from!"hnsecs"(999_000)); +} + + +/+ + Whether the given type defines the static property min which returns the + minimum value for the type. + +/ +template hasMin(T) +{ + enum hasMin = __traits(hasMember, T, "min") && + __traits(isStaticFunction, T.min) && + is(ReturnType!(T.min) == Unqual!T) && + (functionAttributes!(T.min) & FunctionAttribute.PROPERTY) && + (functionAttributes!(T.min) & FunctionAttribute.NOTHROW); + //(functionAttributes!(T.min) & FunctionAttribute.PURE); //Ideally this would be the case, but SysTime's min() can't currently be pure. +} + +unittest +{ + static assert(hasMin!(Date)); + static assert(hasMin!(TimeOfDay)); + static assert(hasMin!(DateTime)); + static assert(hasMin!(SysTime)); + static assert(hasMin!(const Date)); + static assert(hasMin!(const TimeOfDay)); + static assert(hasMin!(const DateTime)); + static assert(hasMin!(const SysTime)); + static assert(hasMin!(immutable Date)); + static assert(hasMin!(immutable TimeOfDay)); + static assert(hasMin!(immutable SysTime)); +} + + +/+ + Whether the given type defines the static property max which returns the + maximum value for the type. + +/ +template hasMax(T) +{ + enum hasMax = __traits(hasMember, T, "max") && + __traits(isStaticFunction, T.max) && + is(ReturnType!(T.max) == Unqual!T) && + (functionAttributes!(T.max) & FunctionAttribute.PROPERTY) && + (functionAttributes!(T.max) & FunctionAttribute.NOTHROW); + //(functionAttributes!(T.max) & FunctionAttribute.PURE); //Ideally this would be the case, but SysTime's max() can't currently be pure. +} + +unittest +{ + static assert(hasMax!(Date)); + static assert(hasMax!(TimeOfDay)); + static assert(hasMax!(DateTime)); + static assert(hasMax!(SysTime)); + static assert(hasMax!(const Date)); + static assert(hasMax!(const TimeOfDay)); + static assert(hasMax!(const DateTime)); + static assert(hasMax!(const SysTime)); + static assert(hasMax!(immutable Date)); + static assert(hasMax!(immutable TimeOfDay)); + static assert(hasMax!(immutable DateTime)); + static assert(hasMax!(immutable SysTime)); +} + + +/+ + Whether the given type defines the overloaded opBinary operators that a time + point is supposed to define which work with time durations. Namely: + + $(TABLE + $(TR $(TD TimePoint opBinary"+"(duration))) + $(TR $(TD TimePoint opBinary"-"(duration))) + ) + +/ +template hasOverloadedOpBinaryWithDuration(T) +{ + enum hasOverloadedOpBinaryWithDuration = __traits(compiles, T.init + dur!"days"(5)) && + is(typeof(T.init + dur!"days"(5)) == Unqual!T) && + __traits(compiles, T.init - dur!"days"(5)) && + is(typeof(T.init - dur!"days"(5)) == Unqual!T) && + __traits(compiles, T.init + TickDuration.from!"hnsecs"(5)) && + is(typeof(T.init + TickDuration.from!"hnsecs"(5)) == Unqual!T) && + __traits(compiles, T.init - TickDuration.from!"hnsecs"(5)) && + is(typeof(T.init - TickDuration.from!"hnsecs"(5)) == Unqual!T); +} + +unittest +{ + static assert(hasOverloadedOpBinaryWithDuration!(Date)); + static assert(hasOverloadedOpBinaryWithDuration!(TimeOfDay)); + static assert(hasOverloadedOpBinaryWithDuration!(DateTime)); + static assert(hasOverloadedOpBinaryWithDuration!(SysTime)); + static assert(hasOverloadedOpBinaryWithDuration!(const Date)); + static assert(hasOverloadedOpBinaryWithDuration!(const TimeOfDay)); + static assert(hasOverloadedOpBinaryWithDuration!(const DateTime)); + static assert(hasOverloadedOpBinaryWithDuration!(const SysTime)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable Date)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable TimeOfDay)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable DateTime)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable SysTime)); +} + + +/+ + Whether the given type defines the overloaded opOpAssign operators that a time point is supposed + to define. Namely: + + $(TABLE + $(TR $(TD TimePoint opOpAssign"+"(duration))) + $(TR $(TD TimePoint opOpAssign"-"(duration))) + ) + +/ +template hasOverloadedOpAssignWithDuration(T) +{ + enum hasOverloadedOpAssignWithDuration = __traits(compiles, T.init += dur!"days"(5)) && + is(typeof(T.init += dur!"days"(5)) == Unqual!T) && + __traits(compiles, T.init -= dur!"days"(5)) && + is(typeof(T.init -= dur!"days"(5)) == Unqual!T) && + __traits(compiles, T.init += TickDuration.from!"hnsecs"(5)) && + is(typeof(T.init += TickDuration.from!"hnsecs"(5)) == Unqual!T) && + __traits(compiles, T.init -= TickDuration.from!"hnsecs"(5)) && + is(typeof(T.init -= TickDuration.from!"hnsecs"(5)) == Unqual!T); +} + +unittest +{ + static assert(hasOverloadedOpAssignWithDuration!(Date)); + static assert(hasOverloadedOpAssignWithDuration!(TimeOfDay)); + static assert(hasOverloadedOpAssignWithDuration!(DateTime)); + static assert(hasOverloadedOpAssignWithDuration!(SysTime)); + static assert(hasOverloadedOpAssignWithDuration!(const Date)); + static assert(hasOverloadedOpAssignWithDuration!(const TimeOfDay)); + static assert(hasOverloadedOpAssignWithDuration!(const DateTime)); + static assert(hasOverloadedOpAssignWithDuration!(const SysTime)); + static assert(hasOverloadedOpAssignWithDuration!(immutable Date)); + static assert(hasOverloadedOpAssignWithDuration!(immutable TimeOfDay)); + static assert(hasOverloadedOpAssignWithDuration!(immutable DateTime)); + static assert(hasOverloadedOpAssignWithDuration!(immutable SysTime)); +} + + +/+ + Whether the given type defines the overloaded opBinary operator that a time point is supposed + to define which works with itself. Namely: + + $(TABLE + $(TR $(TD duration opBinary"-"(Date))) + ) + +/ +template hasOverloadedOpBinaryWithSelf(T) +{ + enum hasOverloadedOpBinaryWithSelf = __traits(compiles, T.init - T.init) && + is(Unqual!(typeof(T.init - T.init)) == Duration); +} + +unittest +{ + static assert(hasOverloadedOpBinaryWithSelf!(Date)); + static assert(hasOverloadedOpBinaryWithSelf!(TimeOfDay)); + static assert(hasOverloadedOpBinaryWithSelf!(DateTime)); + static assert(hasOverloadedOpBinaryWithSelf!(SysTime)); + static assert(hasOverloadedOpBinaryWithSelf!(const Date)); + static assert(hasOverloadedOpBinaryWithSelf!(const TimeOfDay)); + static assert(hasOverloadedOpBinaryWithSelf!(const DateTime)); + static assert(hasOverloadedOpBinaryWithSelf!(const SysTime)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable Date)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable TimeOfDay)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable DateTime)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable SysTime)); +} + +/++ + Unfortunately, to!string() is not pure, so here's a way to convert + a number to a string which is. Once to!string() is properly pure + (like it hopefully will be at some point), this function should + be removed in favor of using to!string(). + +/ +string numToString(long value) pure nothrow +{ + try + { + immutable negative = value < 0; + char[25] str; + size_t i = str.length; + + if(negative) + value = -value; + + while(1) + { + char digit = cast(char)('0' + value % 10); + value /= 10; + + str[--i] = digit; + assert(i > 0); + + if(value == 0) + break; + } + + if(negative) + return "-" ~ str[i .. $].idup; + else + return str[i .. $].idup; + } + catch(Exception e) + assert(0, "Something threw when nothing can throw."); +} + + +/+ + A temporary replacement for Rebindable!() until bug http://d.puremagic.com/issues/show_bug.cgi?id=4977 + is fixed. + +/ +template DTRebindable(T) if (is(T == class) || is(T == interface) || isArray!(T)) +{ + static if(!is(T X == const(U), U) && !is(T X == immutable(U), U)) + { + alias T DTRebindable; + } + else static if(isArray!(T)) + { + alias const(ElementType!(T))[] DTRebindable; + } + else + { + struct DTRebindable + { + private union + { + T original; + U stripped; + } + + void opAssign(T another) pure nothrow + { + stripped = cast(U) another; + } + + void opAssign(DTRebindable another) pure nothrow + { + stripped = another.stripped; + } + + static if(is(T == const U)) + { + // safely assign immutable to const + void opAssign(DTRebindable!(immutable U) another) pure nothrow + { + stripped = another.stripped; + } + } + + this(T initializer) pure nothrow + { + opAssign(initializer); + } + + @property ref T get() pure nothrow + { + return original; + } + + @property ref T get() const pure nothrow + { + return original; + } + + alias get this; + + T opDot() pure nothrow + { + return original; + } + + T opDot() const pure nothrow + { + return original; + } + } + } +} + + +//============================================================================== +// Unit testing functions. +// +// Hopefully a version of these will end up in std.exception soon, and these +// will be able to be removed. Not all of the functions currently under review +// are here, just the ones which std.datetime uses. +//============================================================================== + +void assertThrown(T : Throwable = Exception, F) + (lazy F funcToCall, string msg = null, string file = __FILE__, size_t line = __LINE__) +{ + bool thrown = false; + + try + funcToCall(); + catch(T t) + thrown = true; + + if(!thrown) + { + if(msg.empty) + throw new AssertError(format("assertThrown failed: No %s was thrown.", T.stringof), file, line); + else + throw new AssertError(format("assertThrown failed: No %s was thrown: %s", T.stringof, msg), file, line); + } +} + +unittest +{ + void throwEx(Throwable t) + { + throw t; + } + + void nothrowEx() + { + } + + try + assertThrown!Exception(throwEx(new Exception("It's an Exception"))); + catch(AssertError) + assert(0); + + try + assertThrown!Exception(throwEx(new Exception("It's an Exception")), "It's a message"); + catch(AssertError) + assert(0); + + try + assertThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__))); + catch(AssertError) + assert(0); + + try + assertThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)), "It's a message"); + catch(AssertError) + assert(0); + + + { + bool thrown = false; + try + assertThrown!Exception(nothrowEx()); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + { + bool thrown = false; + try + assertThrown!Exception(nothrowEx(), "It's a message"); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + { + bool thrown = false; + try + assertThrown!AssertError(nothrowEx()); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + { + bool thrown = false; + try + assertThrown!AssertError(nothrowEx(), "It's a message"); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + //Verify Examples. + void timeFunc(int hour, int minute, int second) + { + enforce(hour >= 0 && hour <= 23); + enforce(minute >= 0 && minute <= 59); + enforce(second >= 0 && second <= 59); + + //... + } + + assertThrown!Exception(timeFunc(-1, 15, 30)); + assertThrown!Exception(timeFunc(12, 60, 30)); + assertThrown(timeFunc(12, 15, 60)); //Exception is default. + + + void nameFunc(string name) + { + enforce(!name.empty); + enforce(name.length < 30); + + //... + } + + assertThrown(nameFunc("")); + assertThrown(nameFunc("1234567890123456789012345678901")); + + + void assertPlease() + { + } + + assert(collectExceptionMsg(assertThrown!AssertError(assertPlease())) == + "assertThrown failed: No AssertError was thrown."); + + assert(collectExceptionMsg(assertThrown!AssertError(assertPlease(), "error!")) == + "assertThrown failed: No AssertError was thrown: error!"); +} + + +void assertPred(string op, L, R) + (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) + if((op == "<" || + op == "<=" || + op == "==" || + op == "!=" || + op == ">=" || + op == ">") && + __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && + isPrintable!L && + isPrintable!R) +{ + immutable result = mixin("lhs " ~ op ~ " rhs"); + + if(!result) + { + if(msg.empty) + throw new AssertError(format(`assertPred!"%s" failed: [%s] is not %s [%s].`, op, lhs, op, rhs), file, line); + else + throw new AssertError(format(`assertPred!"%s" failed: [%s] is not %s [%s]: %s`, op, lhs, op, rhs, msg), file, line); + } +} + +unittest +{ + struct IntWrapper + { + int value; + + this(int value) + { + this.value = value; + } + + string toString() + { + return to!string(value); + } + } + + //Test ==. + assertNotThrown!AssertError(assertPred!"=="(6, 6)); + assertNotThrown!AssertError(assertPred!"=="(6, 6.0)); + assertNotThrown!AssertError(assertPred!"=="(IntWrapper(6), IntWrapper(6))); + + assertThrown!AssertError(assertPred!"=="(6, 7)); + assertThrown!AssertError(assertPred!"=="(6, 6.1)); + assertThrown!AssertError(assertPred!"=="(IntWrapper(6), IntWrapper(7))); + assertThrown!AssertError(assertPred!"=="(IntWrapper(7), IntWrapper(6))); + + assertPred!"=="(collectExceptionMsg(assertPred!"=="(6, 7)), + `assertPred!"==" failed: [6] is not == [7].`); + assertPred!"=="(collectExceptionMsg(assertPred!"=="(6, 7, "It failed!")), + `assertPred!"==" failed: [6] is not == [7]: It failed!`); + + //Test !=. + assertNotThrown!AssertError(assertPred!"!="(6, 7)); + assertNotThrown!AssertError(assertPred!"!="(6, 6.1)); + assertNotThrown!AssertError(assertPred!"!="(IntWrapper(6), IntWrapper(7))); + assertNotThrown!AssertError(assertPred!"!="(IntWrapper(7), IntWrapper(6))); + + assertThrown!AssertError(assertPred!"!="(6, 6)); + assertThrown!AssertError(assertPred!"!="(6, 6.0)); + assertThrown!AssertError(assertPred!"!="(IntWrapper(6), IntWrapper(6))); + + assertPred!"=="(collectExceptionMsg(assertPred!"!="(6, 6)), + `assertPred!"!=" failed: [6] is not != [6].`); + assertPred!"=="(collectExceptionMsg(assertPred!"!="(6, 6, "It failed!")), + `assertPred!"!=" failed: [6] is not != [6]: It failed!`); + + //Test <, <=, >=, >. + assertNotThrown!AssertError(assertPred!"<"(5, 7)); + assertNotThrown!AssertError(assertPred!"<="(5, 7)); + assertNotThrown!AssertError(assertPred!"<="(5, 5)); + assertNotThrown!AssertError(assertPred!">="(7, 7)); + assertNotThrown!AssertError(assertPred!">="(7, 5)); + assertNotThrown!AssertError(assertPred!">"(7, 5)); + + assertThrown!AssertError(assertPred!"<"(7, 5)); + assertThrown!AssertError(assertPred!"<="(7, 5)); + assertThrown!AssertError(assertPred!">="(5, 7)); + assertThrown!AssertError(assertPred!">"(5, 7)); + + assertPred!"=="(collectExceptionMsg(assertPred!"<"(7, 5)), + `assertPred!"<" failed: [7] is not < [5].`); + assertPred!"=="(collectExceptionMsg(assertPred!"<"(7, 5, "It failed!")), + `assertPred!"<" failed: [7] is not < [5]: It failed!`); + + //Verify Examples. + + //Equivalent to assert(5 / 2 + 4 < 27); + assertPred!"<"(5 / 2 + 4, 27); + + //Equivalent to assert(4 <= 5); + assertPred!"<="(4, 5); + + //Equivalent to assert(1 * 2.1 == 2.1); + assertPred!"=="(1 * 2.1, 2.1); + + //Equivalent to assert("hello " ~ "world" != "goodbye world"); + assertPred!"!="("hello " ~ "world", "goodbye world"); + + //Equivalent to assert(14.2 >= 14); + assertPred!">="(14.2, 14); + + //Equivalent to assert(15 > 2 + 1); + assertPred!">"(15, 2 + 1); + + assert(collectExceptionMsg(assertPred!"=="("hello", "goodbye")) == + `assertPred!"==" failed: [hello] is not == [goodbye].`); + + assert(collectExceptionMsg(assertPred!"<"(5, 2, "My test failed!")) == + `assertPred!"<" failed: [5] is not < [2]: My test failed!`); +} + + +void assertNotThrown(T : Throwable = Exception, F) + (lazy F funcToCall, string msg = null, string file = __FILE__, size_t line = __LINE__) +{ + try + funcToCall(); + catch(T t) + { + if(msg.empty) + throw new AssertError(format("assertNotThrown failed: %s was thrown.", T.stringof), file, line); + else + throw new AssertError(format("assertNotThrown failed: %s was thrown: %s", T.stringof, msg), file, line); + } +} + +unittest +{ + void throwEx(Throwable t) + { + throw t; + } + + void nothrowEx() + { + } + + try + assertNotThrown!Exception(nothrowEx()); + catch(AssertError) + assert(0); + + try + assertNotThrown!Exception(nothrowEx(), "It's a message"); + catch(AssertError) + assert(0); + + try + assertNotThrown!AssertError(nothrowEx()); + catch(AssertError) + assert(0); + + try + assertNotThrown!AssertError(nothrowEx(), "It's a message"); + catch(AssertError) + assert(0); + + + { + bool thrown = false; + try + assertNotThrown!Exception(throwEx(new Exception("It's an Exception"))); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + { + bool thrown = false; + try + assertNotThrown!Exception(throwEx(new Exception("It's an Exception")), "It's a message"); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + { + bool thrown = false; + try + assertNotThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__))); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + { + bool thrown = false; + try + assertNotThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)), "It's a message"); + catch(AssertError) + thrown = true; + + assert(thrown); + } + + //Verify Examples. + void timeFunc(int hour, int minute, int second) + { + enforce(hour >= 0 && hour <= 23); + enforce(minute >= 0 && minute <= 59); + enforce(second >= 0 && second <= 59); + + //... + } + + assertNotThrown!Exception(timeFunc(0, 0, 0)); + assertNotThrown!Exception(timeFunc(12, 15, 30)); + assertNotThrown(timeFunc(23, 59, 59)); //Exception is default. + + + void nameFunc(string name) + { + enforce(!name.empty); + enforce(name.length < 30); + + //... + } + + assertNotThrown(nameFunc("Walter Bright")); + assertNotThrown(nameFunc("Andrei Alexandrescu")); + + + void assertPlease() + { + assert(1 == 0); + } + + assert(collectExceptionMsg(assertNotThrown!AssertError(assertPlease())) == + "assertNotThrown failed: AssertError was thrown."); + + assert(collectExceptionMsg(assertNotThrown!AssertError(assertPlease(), "error!")) == + "assertNotThrown failed: AssertError was thrown: error!"); +} + + +void assertPred(string func, string expected, L, R) + (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) + if(func == "opCmp" && + (expected == "<" || + expected == "==" || + expected == ">") && + __traits(compiles, lhs.opCmp(rhs)) && + isPrintable!L && + isPrintable!R) +{ + immutable result = lhs.opCmp(rhs); + + static if(expected == "<") + { + if(result < 0) + return; + + if(result == 0) + { + if(msg.empty) + throw new AssertError(format(`assertPred!("opCmp", "<") failed: [%s] == [%s].`, lhs, rhs), file, line); + else + throw new AssertError(format(`assertPred!("opCmp", "<") failed: [%s] == [%s]: %s`, lhs, rhs, msg), file, line); + } + else + { + if(msg.empty) + throw new AssertError(format(`assertPred!("opCmp", "<") failed: [%s] > [%s].`, lhs, rhs), file, line); + else + throw new AssertError(format(`assertPred!("opCmp", "<") failed: [%s] > [%s]: %s`, lhs, rhs, msg), file, line); + } + } + else static if(expected == "==") + { + if(result == 0) + return; + + if(result < 0) + { + if(msg.empty) + throw new AssertError(format(`assertPred!("opCmp", "==") failed: [%s] < [%s].`, lhs, rhs), file, line); + else + throw new AssertError(format(`assertPred!("opCmp", "==") failed: [%s] < [%s]: %s`, lhs, rhs, msg), file, line); + } + else + { + if(msg.empty) + throw new AssertError(format(`assertPred!("opCmp", "==") failed: [%s] > [%s].`, lhs, rhs), file, line); + else + throw new AssertError(format(`assertPred!("opCmp", "==") failed: [%s] > [%s]: %s`, lhs, rhs, msg), file, line); + } + } + else static if(expected == ">") + { + if(result > 0) + return; + + if(result < 0) + { + if(msg.empty) + throw new AssertError(format(`assertPred!("opCmp", ">") failed: [%s] < [%s].`, lhs, rhs), file, line); + else + throw new AssertError(format(`assertPred!("opCmp", ">") failed: [%s] < [%s]: %s`, lhs, rhs, msg), file, line); + } + else + { + if(msg.empty) + throw new AssertError(format(`assertPred!("opCmp", ">") failed: [%s] == [%s].`, lhs, rhs), file, line); + else + throw new AssertError(format(`assertPred!("opCmp", ">") failed: [%s] == [%s]: %s`, lhs, rhs, msg), file, line); + } + } + else + static assert(0); +} + + +void assertPred(string op, L, R, E) + (L lhs, R rhs, E expected, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) + if((op == "+=" || + op == "-=" || + op == "*=" || + op == "/=" || + op == "%=" || + op == "^^=" || + op == "&=" || + op == "|=" || + op == "^=" || + op == "<<=" || + op == ">>=" || + op == ">>>=" || + op == "~=") && + __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && + __traits(compiles, mixin("(lhs " ~ op ~ " rhs) == expected")) && + isPrintable!L && + isPrintable!R) +{ + immutable origLHSStr = to!string(lhs); + const result = mixin("lhs " ~ op ~ " rhs"); + + if(lhs != expected) + { + if(msg.empty) + { + throw new AssertError(format(`assertPred!"%s" failed: After [%s] %s [%s], lhs was assigned to [%s] instead of [%s].`, + op, + origLHSStr, + op, + rhs, + lhs, + expected), + file, + line); + } + else + { + throw new AssertError(format(`assertPred!"%s" failed: After [%s] %s [%s], lhs was assigned to [%s] instead of [%s]: %s`, + op, + origLHSStr, + op, + rhs, + lhs, + expected, + msg), + file, + line); + } + } + + if(result != expected) + { + if(msg.empty) + { + throw new AssertError(format(`assertPred!"%s" failed: Return value of [%s] %s [%s] was [%s] instead of [%s].`, + op, + origLHSStr, + op, + rhs, + result, + expected), + file, + line); + } + else + { + throw new AssertError(format(`assertPred!"%s" failed: Return value of [%s] %s [%s] was [%s] instead of [%s]: %s`, + op, + origLHSStr, + op, + rhs, + result, + expected, + msg), + file, + line); + } + } +} + +unittest +{ + assertNotThrown!AssertError(assertPred!"+="(7, 5, 12)); + assertNotThrown!AssertError(assertPred!"-="(7, 5, 2)); + assertNotThrown!AssertError(assertPred!"*="(7, 5, 35)); + assertNotThrown!AssertError(assertPred!"/="(7, 5, 1)); + assertNotThrown!AssertError(assertPred!"%="(7, 5, 2)); + assertNotThrown!AssertError(assertPred!"^^="(7, 5, 16_807)); + assertNotThrown!AssertError(assertPred!"&="(7, 5, 5)); + assertNotThrown!AssertError(assertPred!"|="(7, 5, 7)); + assertNotThrown!AssertError(assertPred!"^="(7, 5, 2)); + assertNotThrown!AssertError(assertPred!"<<="(7, 1, 14)); + assertNotThrown!AssertError(assertPred!">>="(7, 1, 3)); + assertNotThrown!AssertError(assertPred!">>>="(-7, 1, 2_147_483_644)); + assertNotThrown!AssertError(assertPred!"~="("hello ", "world", "hello world")); + + assertThrown!AssertError(assertPred!"+="(7, 5, 0)); + assertThrown!AssertError(assertPred!"-="(7, 5, 0)); + assertThrown!AssertError(assertPred!"*="(7, 5, 0)); + assertThrown!AssertError(assertPred!"/="(7, 5, 0)); + assertThrown!AssertError(assertPred!"%="(7, 5, 0)); + assertThrown!AssertError(assertPred!"^^="(7, 5, 0)); + assertThrown!AssertError(assertPred!"&="(7, 5, 0)); + assertThrown!AssertError(assertPred!"|="(7, 5, 0)); + assertThrown!AssertError(assertPred!"^="(7, 5, 0)); + assertThrown!AssertError(assertPred!"<<="(7, 1, 0)); + assertThrown!AssertError(assertPred!">>="(7, 1, 0)); + assertThrown!AssertError(assertPred!">>>="(-7, 1, 0)); + assertThrown!AssertError(assertPred!"~="("hello ", "world", "goodbye world")); + + assertPred!"=="(collectExceptionMsg(assertPred!"+="(7, 5, 11)), + `assertPred!"+=" failed: After [7] += [5], lhs was assigned to [12] instead of [11].`); + assertPred!"=="(collectExceptionMsg(assertPred!"+="(7, 5, 11, "It failed!")), + `assertPred!"+=" failed: After [7] += [5], lhs was assigned to [12] instead of [11]: It failed!`); + + assertPred!"=="(collectExceptionMsg(assertPred!"^^="(7, 5, 42)), + `assertPred!"^^=" failed: After [7] ^^= [5], lhs was assigned to [16807] instead of [42].`); + assertPred!"=="(collectExceptionMsg(assertPred!"^^="(7, 5, 42, "It failed!")), + `assertPred!"^^=" failed: After [7] ^^= [5], lhs was assigned to [16807] instead of [42]: It failed!`); + + assertPred!"=="(collectExceptionMsg(assertPred!"~="("hello ", "world", "goodbye world")), + `assertPred!"~=" failed: After [hello ] ~= [world], lhs was assigned to [hello world] instead of [goodbye world].`); + assertPred!"=="(collectExceptionMsg(assertPred!"~="("hello ", "world", "goodbye world", "It failed!")), + `assertPred!"~=" failed: After [hello ] ~= [world], lhs was assigned to [hello world] instead of [goodbye world]: It failed!`); + + struct IntWrapper + { + int value; + + this(int value) + { + this.value = value; + } + + IntWrapper opOpAssign(string op)(IntWrapper rhs) + { + mixin("this.value " ~ op ~ "= rhs.value;"); + + return this; + } + + string toString() + { + return to!string(value); + } + } + + struct IntWrapper_BadAssign + { + int value; + + this(int value) + { + this.value = value; + } + + IntWrapper_BadAssign opOpAssign(string op)(IntWrapper_BadAssign rhs) + { + auto old = this.value; + + mixin("this.value " ~ op ~ "= -rhs.value;"); + + return IntWrapper_BadAssign(mixin("old " ~ op ~ " rhs.value")); + } + + string toString() + { + return to!string(value); + } + } + + struct IntWrapper_BadReturn + { + int value; + + this(int value) + { + this.value = value; + } + + IntWrapper_BadReturn opOpAssign(string op)(IntWrapper_BadReturn rhs) + { + mixin("this.value " ~ op ~ "= rhs.value;"); + + return IntWrapper_BadReturn(rhs.value); + } + + string toString() + { + return to!string(value); + } + } + + assertNotThrown!AssertError(assertPred!"+="(IntWrapper(5), IntWrapper(2), IntWrapper(7))); + assertNotThrown!AssertError(assertPred!"*="(IntWrapper(5), IntWrapper(2), IntWrapper(10))); + + assertThrown!AssertError(assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7))); + assertThrown!AssertError(assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7))); + assertThrown!AssertError(assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10))); + assertThrown!AssertError(assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10))); + + assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7))), + `assertPred!"+=" failed: After [5] += [2], lhs was assigned to [3] instead of [7].`); + assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7), "It failed!")), + `assertPred!"+=" failed: After [5] += [2], lhs was assigned to [3] instead of [7]: It failed!`); + + assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7))), + `assertPred!"+=" failed: Return value of [5] += [2] was [2] instead of [7].`); + assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7), "It failed!")), + `assertPred!"+=" failed: Return value of [5] += [2] was [2] instead of [7]: It failed!`); + + assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10))), + `assertPred!"*=" failed: After [5] *= [2], lhs was assigned to [-10] instead of [10].`); + assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10), "It failed!")), + `assertPred!"*=" failed: After [5] *= [2], lhs was assigned to [-10] instead of [10]: It failed!`); + + assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10))), + `assertPred!"*=" failed: Return value of [5] *= [2] was [2] instead of [10].`); + assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10), "It failed!")), + `assertPred!"*=" failed: Return value of [5] *= [2] was [2] instead of [10]: It failed!`); +} + + +string collectExceptionMsg(T)(lazy T funcCall) +{ + try + { + funcCall(); + + return cast(string)null; + } + catch(Throwable t) + return t.msg; +} + +unittest +{ + //Verify Example. + void throwFunc() {throw new Exception("My Message.");} + assert(collectExceptionMsg(throwFunc()) == "My Message."); + + void nothrowFunc() {} + assert(collectExceptionMsg(nothrowFunc()) is null); +} + + +template isPrintable(T...) +{ + static if(T.length == 0) + enum isPrintable = true; + else static if(T.length == 1) + { + enum isPrintable = (!isArray!(T[0]) && __traits(compiles, to!string(T[0].init))) || + (isArray!(T[0]) && __traits(compiles, to!string(T[0].init[0]))); + } + else + { + enum isPrintable = isPrintable!(T[0]) && isPrintable!(T[1 .. $]); + } +}