Home | History | Annotate | Line # | Download | only in datetime
      1 // Written in the D programming language
      2 /++
      3 
      4 $(SCRIPT inhibitQuickIndex = 1;)
      5 $(DIVC quickindex,
      6 $(BOOKTABLE,
      7 $(TR $(TH Category) $(TH Functions))
      8 $(TR $(TD Main date types) $(TD
      9     $(LREF Date)
     10     $(LREF DateTime)
     11 ))
     12 $(TR $(TD Other date types) $(TD
     13     $(LREF Month)
     14     $(LREF DayOfWeek)
     15     $(LREF TimeOfDay)
     16 ))
     17 $(TR $(TD Date checking) $(TD
     18     $(LREF valid)
     19     $(LREF validTimeUnits)
     20     $(LREF yearIsLeapYear)
     21     $(LREF isTimePoint)
     22     $(LREF enforceValid)
     23 ))
     24 $(TR $(TD Date conversion) $(TD
     25     $(LREF daysToDayOfWeek)
     26     $(LREF monthsToMonth)
     27 ))
     28 $(TR $(TD Time units) $(TD
     29     $(LREF cmpTimeUnits)
     30     $(LREF timeStrings)
     31 ))
     32 $(TR $(TD Other) $(TD
     33     $(LREF AllowDayOverflow)
     34     $(LREF DateTimeException)
     35 ))
     36 ))
     37 
     38     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
     39     Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis)
     40     Source:    $(PHOBOSSRC std/datetime/date.d)
     41 +/
     42 module std.datetime.date;
     43 
     44 import core.time : TimeException;
     45 import std.traits : isSomeString, Unqual;
     46 import std.typecons : Flag;
     47 import std.range.primitives : isOutputRange;
     48 
     49 version (StdUnittest) import std.exception : assertThrown;
     50 
     51 @safe unittest
     52 {
     53     initializeTests();
     54 }
     55 
     56 
     57 /++
     58     Exception type used by std.datetime. It's an alias to
     59     $(REF TimeException,core,time). Either can be caught without concern about
     60     which module it came from.
     61   +/
     62 alias DateTimeException = TimeException;
     63 
     64 
     65 /++
     66     Represents the 12 months of the Gregorian year (January is 1).
     67   +/
     68 enum Month : ubyte
     69 {
     70     jan = 1, ///
     71     feb,     ///
     72     mar,     ///
     73     apr,     ///
     74     may,     ///
     75     jun,     ///
     76     jul,     ///
     77     aug,     ///
     78     sep,     ///
     79     oct,     ///
     80     nov,     ///
     81     dec      ///
     82 }
     83 
     84 ///
     85 @safe pure unittest
     86 {
     87     assert(Date(2018, 10, 1).month == Month.oct);
     88     assert(DateTime(1, 1, 1).month == Month.jan);
     89 }
     90 
     91 
     92 /++
     93     Represents the 7 days of the Gregorian week (Sunday is 0).
     94   +/
     95 enum DayOfWeek : ubyte
     96 {
     97     sun = 0, ///
     98     mon,     ///
     99     tue,     ///
    100     wed,     ///
    101     thu,     ///
    102     fri,     ///
    103     sat      ///
    104 }
    105 
    106 ///
    107 @safe pure unittest
    108 {
    109     assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
    110     assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
    111 }
    112 
    113 /++
    114     In some date calculations, adding months or years can cause the date to fall
    115     on a day of the month which is not valid (e.g. February 29th 2001 or
    116     June 31st 2000). If overflow is allowed (as is the default), then the month
    117     will be incremented accordingly (so, February 29th 2001 would become
    118     March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
    119     is not allowed, then the day will be adjusted to the last valid day in that
    120     month (so, February 29th 2001 would become February 28th 2001 and
    121     June 31st 2000 would become June 30th 2000).
    122 
    123     AllowDayOverflow only applies to calculations involving months or years.
    124 
    125     If set to `AllowDayOverflow.no`, then day overflow is not allowed.
    126 
    127     Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
    128     allowed.
    129   +/
    130 alias AllowDayOverflow = Flag!"allowDayOverflow";
    131 
    132 
    133 /++
    134     Array of the strings representing time units, starting with the smallest
    135     unit and going to the largest. It does not include `"nsecs"`.
    136 
    137     Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
    138     `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
    139     `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
    140     `"years"`
    141   +/
    142 immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
    143                                   "hours", "days", "weeks", "months", "years"];
    144 
    145 
    146 /++
    147     Combines the $(REF Date,std,datetime,date) and
    148     $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
    149     both the date and the time. It is optimized for calendar-based operations
    150     and has no concept of time zone. For an object which is optimized for time
    151     operations based on the system time, use
    152     $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
    153     a concept of time zone and has much higher precision (hnsecs). `DateTime`
    154     is intended primarily for calendar-based uses rather than precise time
    155     operations.
    156   +/
    157 struct DateTime
    158 {
    159 public:
    160 
    161     /++
    162         Params:
    163             date = The date portion of $(LREF DateTime).
    164             tod  = The time portion of $(LREF DateTime).
    165       +/
    166     this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
    167     {
    168         _date = date;
    169         _tod = tod;
    170     }
    171 
    172     @safe unittest
    173     {
    174         {
    175             auto dt = DateTime.init;
    176             assert(dt._date == Date.init);
    177             assert(dt._tod == TimeOfDay.init);
    178         }
    179 
    180         {
    181             auto dt = DateTime(Date(1999, 7 ,6));
    182             assert(dt._date == Date(1999, 7, 6));
    183             assert(dt._tod == TimeOfDay.init);
    184         }
    185 
    186         {
    187             auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33));
    188             assert(dt._date == Date(1999, 7, 6));
    189             assert(dt._tod == TimeOfDay(12, 30, 33));
    190         }
    191     }
    192 
    193 
    194     /++
    195         Params:
    196             year   = The year portion of the date.
    197             month  = The month portion of the date (January is 1).
    198             day    = The day portion of the date.
    199             hour   = The hour portion of the time;
    200             minute = The minute portion of the time;
    201             second = The second portion of the time;
    202       +/
    203     this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure
    204     {
    205         _date = Date(year, month, day);
    206         _tod = TimeOfDay(hour, minute, second);
    207     }
    208 
    209     @safe unittest
    210     {
    211         {
    212             auto dt = DateTime(1999, 7 ,6);
    213             assert(dt._date == Date(1999, 7, 6));
    214             assert(dt._tod == TimeOfDay.init);
    215         }
    216 
    217         {
    218             auto dt = DateTime(1999, 7 ,6, 12, 30, 33);
    219             assert(dt._date == Date(1999, 7, 6));
    220             assert(dt._tod == TimeOfDay(12, 30, 33));
    221         }
    222     }
    223 
    224 
    225     /++
    226         Compares this $(LREF DateTime) with the given `DateTime.`.
    227 
    228         Returns:
    229             $(BOOKTABLE,
    230             $(TR $(TD this < rhs) $(TD < 0))
    231             $(TR $(TD this == rhs) $(TD 0))
    232             $(TR $(TD this > rhs) $(TD > 0))
    233             )
    234      +/
    235     int opCmp(DateTime rhs) const @safe pure nothrow @nogc
    236     {
    237         immutable dateResult = _date.opCmp(rhs._date);
    238 
    239         if (dateResult != 0)
    240             return dateResult;
    241 
    242         return _tod.opCmp(rhs._tod);
    243     }
    244 
    245     @safe unittest
    246     {
    247         // Test A.D.
    248         assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0);
    249 
    250         assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0);
    251         assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0);
    252         assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0);
    253 
    254         assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0);
    255         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0);
    256 
    257         assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0);
    258 
    259         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
    260         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
    261         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
    262         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
    263         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0);
    264         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
    265 
    266         assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
    267         assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
    268         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
    269         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
    270         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
    271         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
    272 
    273 
    274         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp(
    275                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0);
    276         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp(
    277                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0);
    278         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp(
    279                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0);
    280         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
    281                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
    282 
    283         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp(
    284                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0);
    285         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    286                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
    287 
    288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp(
    289                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0);
    290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
    291                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
    292 
    293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    294                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
    295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
    296                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    298                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
    299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
    300                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    302                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0);
    303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
    304                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    305 
    306         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
    307                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
    308         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
    309                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
    310         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
    311                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
    312         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
    313                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
    314 
    315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
    316                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
    317         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
    318                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
    319 
    320         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
    321                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    322         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    323                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
    324         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
    325                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    326         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    327                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
    328         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
    329                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    330         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    331                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
    332 
    333         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
    334                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
    335         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    336                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
    337         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
    338                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
    339         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    340                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
    341         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
    342                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
    343         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    344                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
    345 
    346         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
    347                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
    348         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    349                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
    350         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
    351                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0);
    352         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    353                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    354         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
    355                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
    356         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    357                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
    358 
    359         // Test B.C.
    360         assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp(
    361                    DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0);
    362         assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
    363                    DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0);
    364         assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp(
    365                    DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0);
    366 
    367         assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
    368                    DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0);
    369         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    370                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
    371 
    372         assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    373                    DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0);
    374 
    375         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    376                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    377         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    378                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    379         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    380                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
    381         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    382                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    383         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    384                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
    385         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    386                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    387 
    388         assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    389                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
    390         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
    391                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    392         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    393                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
    394         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    395                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    396         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    397                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
    398         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    399                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
    400 
    401         // Test Both
    402         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    403                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    404         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    405                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    406 
    407         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    408                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    409         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    410                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0);
    411 
    412         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
    413                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    414         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    415                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
    416 
    417         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
    418                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
    419         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
    420                    DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0);
    421 
    422         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
    423                    DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0);
    424         assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp(
    425                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
    426 
    427         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
    428         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
    429         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
    430         assert(dt.opCmp(dt) == 0);
    431         assert(dt.opCmp(cdt) == 0);
    432         assert(dt.opCmp(idt) == 0);
    433         assert(cdt.opCmp(dt) == 0);
    434         assert(cdt.opCmp(cdt) == 0);
    435         assert(cdt.opCmp(idt) == 0);
    436         assert(idt.opCmp(dt) == 0);
    437         assert(idt.opCmp(cdt) == 0);
    438         assert(idt.opCmp(idt) == 0);
    439     }
    440 
    441 
    442     /++
    443         The date portion of $(LREF DateTime).
    444       +/
    445     @property Date date() const @safe pure nothrow @nogc
    446     {
    447         return _date;
    448     }
    449 
    450     @safe unittest
    451     {
    452         {
    453             auto dt = DateTime.init;
    454             assert(dt.date == Date.init);
    455         }
    456 
    457         {
    458             auto dt = DateTime(Date(1999, 7, 6));
    459             assert(dt.date == Date(1999, 7, 6));
    460         }
    461 
    462         const cdt = DateTime(1999, 7, 6);
    463         immutable idt = DateTime(1999, 7, 6);
    464         assert(cdt.date == Date(1999, 7, 6));
    465         assert(idt.date == Date(1999, 7, 6));
    466     }
    467 
    468 
    469     /++
    470         The date portion of $(LREF DateTime).
    471 
    472         Params:
    473             date = The Date to set this $(LREF DateTime)'s date portion to.
    474       +/
    475     @property void date(Date date) @safe pure nothrow @nogc
    476     {
    477         _date = date;
    478     }
    479 
    480     @safe unittest
    481     {
    482         auto dt = DateTime.init;
    483         dt.date = Date(1999, 7, 6);
    484         assert(dt._date == Date(1999, 7, 6));
    485         assert(dt._tod == TimeOfDay.init);
    486 
    487         const cdt = DateTime(1999, 7, 6);
    488         immutable idt = DateTime(1999, 7, 6);
    489         static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1)));
    490         static assert(!__traits(compiles, idt.date = Date(2010, 1, 1)));
    491     }
    492 
    493 
    494     /++
    495         The time portion of $(LREF DateTime).
    496       +/
    497     @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
    498     {
    499         return _tod;
    500     }
    501 
    502     @safe unittest
    503     {
    504         {
    505             auto dt = DateTime.init;
    506             assert(dt.timeOfDay == TimeOfDay.init);
    507         }
    508 
    509         {
    510             auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33));
    511             assert(dt.timeOfDay == TimeOfDay(12, 30, 33));
    512         }
    513 
    514         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    515         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    516         assert(cdt.timeOfDay == TimeOfDay(12, 30, 33));
    517         assert(idt.timeOfDay == TimeOfDay(12, 30, 33));
    518     }
    519 
    520 
    521     /++
    522         The time portion of $(LREF DateTime).
    523 
    524         Params:
    525             tod = The $(REF TimeOfDay,std,datetime,date) to set this
    526                   $(LREF DateTime)'s time portion to.
    527       +/
    528     @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
    529     {
    530         _tod = tod;
    531     }
    532 
    533     @safe unittest
    534     {
    535         auto dt = DateTime.init;
    536         dt.timeOfDay = TimeOfDay(12, 30, 33);
    537         assert(dt._date == Date.init);
    538         assert(dt._tod == TimeOfDay(12, 30, 33));
    539 
    540         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    541         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    542         static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33)));
    543         static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33)));
    544     }
    545 
    546 
    547     /++
    548         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
    549         are B.C.
    550      +/
    551     @property short year() const @safe pure nothrow @nogc
    552     {
    553         return _date.year;
    554     }
    555 
    556     @safe unittest
    557     {
    558         assert(Date.init.year == 1);
    559         assert(Date(1999, 7, 6).year == 1999);
    560         assert(Date(-1999, 7, 6).year == -1999);
    561 
    562         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    563         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    564         assert(idt.year == 1999);
    565         assert(idt.year == 1999);
    566     }
    567 
    568 
    569     /++
    570         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
    571         are B.C.
    572 
    573         Params:
    574             year = The year to set this $(LREF DateTime)'s year to.
    575 
    576         Throws:
    577             $(REF DateTimeException,std,datetime,date) if the new year is not
    578             a leap year and if the resulting date would be on February 29th.
    579      +/
    580     @property void year(int year) @safe pure
    581     {
    582         _date.year = year;
    583     }
    584 
    585     ///
    586     @safe unittest
    587     {
    588         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
    589         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
    590         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
    591     }
    592 
    593     @safe unittest
    594     {
    595         static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
    596         {
    597             dt.year = year;
    598             assert(dt == expected);
    599         }
    600 
    601         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
    602                1999,
    603                DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)));
    604         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
    605                0,
    606                DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)));
    607         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
    608                -1999,
    609                DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)));
    610 
    611         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    612         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    613         static assert(!__traits(compiles, cdt.year = 7));
    614         static assert(!__traits(compiles, idt.year = 7));
    615     }
    616 
    617 
    618     /++
    619         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
    620 
    621         Throws:
    622             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
    623      +/
    624     @property short yearBC() const @safe pure
    625     {
    626         return _date.yearBC;
    627     }
    628 
    629     ///
    630     @safe unittest
    631     {
    632         assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
    633         assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
    634         assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
    635     }
    636 
    637     @safe unittest
    638     {
    639         assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
    640 
    641         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
    642         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    643         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    644         dt.yearBC = 12;
    645         assert(dt.yearBC == 12);
    646         static assert(!__traits(compiles, cdt.yearBC = 12));
    647         static assert(!__traits(compiles, idt.yearBC = 12));
    648     }
    649 
    650 
    651     /++
    652         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
    653 
    654         Params:
    655             year = The year B.C. to set this $(LREF DateTime)'s year to.
    656 
    657         Throws:
    658             $(REF DateTimeException,std,datetime,date) if a non-positive value
    659             is given.
    660      +/
    661     @property void yearBC(int year) @safe pure
    662     {
    663         _date.yearBC = year;
    664     }
    665 
    666     ///
    667     @safe unittest
    668     {
    669         auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
    670         dt.yearBC = 1;
    671         assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
    672 
    673         dt.yearBC = 10;
    674         assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
    675     }
    676 
    677     @safe unittest
    678     {
    679         assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1))));
    680 
    681         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
    682         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    683         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    684         dt.yearBC = 12;
    685         assert(dt.yearBC == 12);
    686         static assert(!__traits(compiles, cdt.yearBC = 12));
    687         static assert(!__traits(compiles, idt.yearBC = 12));
    688     }
    689 
    690 
    691     /++
    692         Month of a Gregorian Year.
    693      +/
    694     @property Month month() const @safe pure nothrow @nogc
    695     {
    696         return _date.month;
    697     }
    698 
    699     ///
    700     @safe unittest
    701     {
    702         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
    703         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
    704         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
    705     }
    706 
    707     @safe unittest
    708     {
    709         assert(DateTime.init.month == 1);
    710         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
    711         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
    712 
    713         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    714         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    715         assert(cdt.month == 7);
    716         assert(idt.month == 7);
    717     }
    718 
    719 
    720     /++
    721         Month of a Gregorian Year.
    722 
    723         Params:
    724             month = The month to set this $(LREF DateTime)'s month to.
    725 
    726         Throws:
    727             $(REF DateTimeException,std,datetime,date) if the given month is
    728             not a valid month.
    729      +/
    730     @property void month(Month month) @safe pure
    731     {
    732         _date.month = month;
    733     }
    734 
    735     @safe unittest
    736     {
    737         static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
    738         {
    739             dt.month = month;
    740             assert(expected != DateTime.init);
    741             assert(dt == expected);
    742         }
    743 
    744         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0));
    745         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13));
    746 
    747         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
    748                cast(Month) 7,
    749                DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)));
    750         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)),
    751                cast(Month) 7,
    752                DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)));
    753 
    754         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    755         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    756         static assert(!__traits(compiles, cdt.month = 12));
    757         static assert(!__traits(compiles, idt.month = 12));
    758     }
    759 
    760 
    761     /++
    762         Day of a Gregorian Month.
    763      +/
    764     @property ubyte day() const @safe pure nothrow @nogc
    765     {
    766         return _date.day;
    767     }
    768 
    769     ///
    770     @safe unittest
    771     {
    772         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
    773         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
    774         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
    775     }
    776 
    777     @safe unittest
    778     {
    779         import std.format : format;
    780         import std.range : chain;
    781 
    782         static void test(DateTime dateTime, int expected)
    783         {
    784             assert(dateTime.day == expected, format("Value given: %s", dateTime));
    785         }
    786 
    787         foreach (year; chain(testYearsBC, testYearsAD))
    788         {
    789             foreach (md; testMonthDays)
    790             {
    791                 foreach (tod; testTODs)
    792                     test(DateTime(Date(year, md.month, md.day), tod), md.day);
    793             }
    794         }
    795 
    796         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    797         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    798         assert(cdt.day == 6);
    799         assert(idt.day == 6);
    800     }
    801 
    802 
    803     /++
    804         Day of a Gregorian Month.
    805 
    806         Params:
    807             day = The day of the month to set this $(LREF DateTime)'s day to.
    808 
    809         Throws:
    810             $(REF DateTimeException,std,datetime,date) if the given day is not
    811             a valid day of the current month.
    812      +/
    813     @property void day(int day) @safe pure
    814     {
    815         _date.day = day;
    816     }
    817 
    818     @safe unittest
    819     {
    820         import std.exception : assertNotThrown;
    821 
    822         static void testDT(DateTime dt, int day)
    823         {
    824             dt.day = day;
    825         }
    826 
    827         // Test A.D.
    828         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0));
    829         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32));
    830         assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29));
    831         assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30));
    832         assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32));
    833         assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31));
    834         assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32));
    835         assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31));
    836         assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32));
    837         assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32));
    838         assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31));
    839         assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32));
    840         assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31));
    841         assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32));
    842 
    843         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31));
    844         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28));
    845         assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29));
    846         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31));
    847         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30));
    848         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31));
    849         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30));
    850         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31));
    851         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31));
    852         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30));
    853         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31));
    854         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30));
    855         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31));
    856 
    857         {
    858             auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22));
    859             dt.day = 6;
    860             assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22)));
    861         }
    862 
    863         // Test B.C.
    864         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0));
    865         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32));
    866         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29));
    867         assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30));
    868         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32));
    869         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31));
    870         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32));
    871         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31));
    872         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32));
    873         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32));
    874         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31));
    875         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32));
    876         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31));
    877         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32));
    878 
    879         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31));
    880         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28));
    881         assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29));
    882         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31));
    883         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30));
    884         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31));
    885         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30));
    886         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31));
    887         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31));
    888         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30));
    889         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31));
    890         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30));
    891         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31));
    892 
    893         auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22));
    894         dt.day = 6;
    895         assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22)));
    896 
    897         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    898         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    899         static assert(!__traits(compiles, cdt.day = 27));
    900         static assert(!__traits(compiles, idt.day = 27));
    901     }
    902 
    903 
    904     /++
    905         Hours past midnight.
    906      +/
    907     @property ubyte hour() const @safe pure nothrow @nogc
    908     {
    909         return _tod.hour;
    910     }
    911 
    912     @safe unittest
    913     {
    914         assert(DateTime.init.hour == 0);
    915         assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12);
    916 
    917         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    918         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    919         assert(cdt.hour == 12);
    920         assert(idt.hour == 12);
    921     }
    922 
    923 
    924     /++
    925         Hours past midnight.
    926 
    927         Params:
    928             hour = The hour of the day to set this $(LREF DateTime)'s hour to.
    929 
    930         Throws:
    931             $(REF DateTimeException,std,datetime,date) if the given hour would
    932             result in an invalid $(LREF DateTime).
    933      +/
    934     @property void hour(int hour) @safe pure
    935     {
    936         _tod.hour = hour;
    937     }
    938 
    939     @safe unittest
    940     {
    941         assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}());
    942 
    943         auto dt = DateTime.init;
    944         dt.hour = 12;
    945         assert(dt == DateTime(1, 1, 1, 12, 0, 0));
    946 
    947         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    948         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    949         static assert(!__traits(compiles, cdt.hour = 27));
    950         static assert(!__traits(compiles, idt.hour = 27));
    951     }
    952 
    953 
    954     /++
    955         Minutes past the hour.
    956      +/
    957     @property ubyte minute() const @safe pure nothrow @nogc
    958     {
    959         return _tod.minute;
    960     }
    961 
    962     @safe unittest
    963     {
    964         assert(DateTime.init.minute == 0);
    965         assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30);
    966 
    967         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    968         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    969         assert(cdt.minute == 30);
    970         assert(idt.minute == 30);
    971     }
    972 
    973 
    974     /++
    975         Minutes past the hour.
    976 
    977         Params:
    978             minute = The minute to set this $(LREF DateTime)'s minute to.
    979 
    980         Throws:
    981             $(REF DateTimeException,std,datetime,date) if the given minute
    982             would result in an invalid $(LREF DateTime).
    983      +/
    984     @property void minute(int minute) @safe pure
    985     {
    986         _tod.minute = minute;
    987     }
    988 
    989     @safe unittest
    990     {
    991         assertThrown!DateTimeException((){DateTime.init.minute = 60;}());
    992 
    993         auto dt = DateTime.init;
    994         dt.minute = 30;
    995         assert(dt == DateTime(1, 1, 1, 0, 30, 0));
    996 
    997         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
    998         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
    999         static assert(!__traits(compiles, cdt.minute = 27));
   1000         static assert(!__traits(compiles, idt.minute = 27));
   1001     }
   1002 
   1003 
   1004     /++
   1005         Seconds past the minute.
   1006      +/
   1007     @property ubyte second() const @safe pure nothrow @nogc
   1008     {
   1009         return _tod.second;
   1010     }
   1011 
   1012     @safe unittest
   1013     {
   1014         assert(DateTime.init.second == 0);
   1015         assert(DateTime(1, 1, 1, 0, 0, 33).second == 33);
   1016 
   1017         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1018         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1019         assert(cdt.second == 33);
   1020         assert(idt.second == 33);
   1021     }
   1022 
   1023 
   1024     /++
   1025         Seconds past the minute.
   1026 
   1027         Params:
   1028             second = The second to set this $(LREF DateTime)'s second to.
   1029 
   1030         Throws:
   1031             $(REF DateTimeException,std,datetime,date) if the given seconds
   1032             would result in an invalid $(LREF DateTime).
   1033      +/
   1034     @property void second(int second) @safe pure
   1035     {
   1036         _tod.second = second;
   1037     }
   1038 
   1039     @safe unittest
   1040     {
   1041         assertThrown!DateTimeException((){DateTime.init.second = 60;}());
   1042 
   1043         auto dt = DateTime.init;
   1044         dt.second = 33;
   1045         assert(dt == DateTime(1, 1, 1, 0, 0, 33));
   1046 
   1047         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1048         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1049         static assert(!__traits(compiles, cdt.second = 27));
   1050         static assert(!__traits(compiles, idt.second = 27));
   1051     }
   1052 
   1053 
   1054     /++
   1055         Adds the given number of years or months to this $(LREF DateTime),
   1056         mutating it. A negative number will subtract.
   1057 
   1058         Note that if day overflow is allowed, and the date with the adjusted
   1059         year/month overflows the number of days in the new month, then the month
   1060         will be incremented by one, and the day set to the number of days
   1061         overflowed. (e.g. if the day were 31 and the new month were June, then
   1062         the month would be incremented to July, and the new day would be 1). If
   1063         day overflow is not allowed, then the day will be set to the last valid
   1064         day in the month (e.g. June 31st would become June 30th).
   1065 
   1066         Params:
   1067             units         = The type of units to add ("years" or "months").
   1068             value         = The number of months or years to add to this
   1069                             $(LREF DateTime).
   1070             allowOverflow = Whether the days should be allowed to overflow,
   1071                             causing the month to increment.
   1072 
   1073         Returns:
   1074             A reference to the `DateTime` (`this`).
   1075       +/
   1076     ref DateTime add(string units)
   1077                     (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
   1078         if (units == "years" || units == "months")
   1079     {
   1080         _date.add!units(value, allowOverflow);
   1081         return this;
   1082     }
   1083 
   1084     ///
   1085     @safe unittest
   1086     {
   1087         auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
   1088         dt1.add!"months"(11);
   1089         assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
   1090 
   1091         auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
   1092         dt2.add!"months"(-11);
   1093         assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
   1094 
   1095         auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
   1096         dt3.add!"years"(1);
   1097         assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
   1098 
   1099         auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
   1100         dt4.add!"years"(1, AllowDayOverflow.no);
   1101         assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
   1102     }
   1103 
   1104     @safe unittest
   1105     {
   1106         auto dt = DateTime(2000, 1, 31);
   1107         dt.add!"years"(7).add!"months"(-4);
   1108         assert(dt == DateTime(2006, 10, 1));
   1109 
   1110         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1111         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1112         static assert(!__traits(compiles, cdt.add!"years"(4)));
   1113         static assert(!__traits(compiles, idt.add!"years"(4)));
   1114         static assert(!__traits(compiles, cdt.add!"months"(4)));
   1115         static assert(!__traits(compiles, idt.add!"months"(4)));
   1116     }
   1117 
   1118 
   1119     /++
   1120         Adds the given number of years or months to this $(LREF DateTime),
   1121         mutating it. A negative number will subtract.
   1122 
   1123         The difference between rolling and adding is that rolling does not
   1124         affect larger units. Rolling a $(LREF DateTime) 12 months
   1125         gets the exact same $(LREF DateTime). However, the days can still be
   1126         affected due to the differing number of days in each month.
   1127 
   1128         Because there are no units larger than years, there is no difference
   1129         between adding and rolling years.
   1130 
   1131         Params:
   1132             units         = The type of units to add ("years" or "months").
   1133             value         = The number of months or years to add to this
   1134                             $(LREF DateTime).
   1135             allowOverflow = Whether the days should be allowed to overflow,
   1136                             causing the month to increment.
   1137 
   1138         Returns:
   1139             A reference to the `DateTime` (`this`).
   1140       +/
   1141     ref DateTime roll(string units)
   1142                      (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
   1143         if (units == "years" || units == "months")
   1144     {
   1145         _date.roll!units(value, allowOverflow);
   1146         return this;
   1147     }
   1148 
   1149     ///
   1150     @safe unittest
   1151     {
   1152         auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
   1153         dt1.roll!"months"(1);
   1154         assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
   1155 
   1156         auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
   1157         dt2.roll!"months"(-1);
   1158         assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
   1159 
   1160         auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
   1161         dt3.roll!"months"(1);
   1162         assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
   1163 
   1164         auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
   1165         dt4.roll!"months"(1, AllowDayOverflow.no);
   1166         assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
   1167 
   1168         auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
   1169         dt5.roll!"years"(1);
   1170         assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
   1171 
   1172         auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
   1173         dt6.roll!"years"(1, AllowDayOverflow.no);
   1174         assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
   1175     }
   1176 
   1177     @safe unittest
   1178     {
   1179         auto dt = DateTime(2000, 1, 31);
   1180         dt.roll!"years"(7).roll!"months"(-4);
   1181         assert(dt == DateTime(2007, 10, 1));
   1182 
   1183         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1184         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1185         static assert(!__traits(compiles, cdt.roll!"years"(4)));
   1186         static assert(!__traits(compiles, idt.roll!"years"(4)));
   1187         static assert(!__traits(compiles, cdt.roll!"months"(4)));
   1188         static assert(!__traits(compiles, idt.roll!"months"(4)));
   1189     }
   1190 
   1191 
   1192     /++
   1193         Adds the given number of units to this $(LREF DateTime), mutating it. A
   1194         negative number will subtract.
   1195 
   1196         The difference between rolling and adding is that rolling does not
   1197         affect larger units. For instance, rolling a $(LREF DateTime) one
   1198         year's worth of days gets the exact same $(LREF DateTime).
   1199 
   1200         Accepted units are `"days"`, `"minutes"`, `"hours"`,
   1201         `"minutes"`, and `"seconds"`.
   1202 
   1203         Params:
   1204             units = The units to add.
   1205             value = The number of $(D_PARAM units) to add to this
   1206                     $(LREF DateTime).
   1207 
   1208         Returns:
   1209             A reference to the `DateTime` (`this`).
   1210       +/
   1211     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
   1212         if (units == "days")
   1213     {
   1214         _date.roll!"days"(value);
   1215         return this;
   1216     }
   1217 
   1218     ///
   1219     @safe unittest
   1220     {
   1221         auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
   1222         dt1.roll!"days"(1);
   1223         assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
   1224         dt1.roll!"days"(365);
   1225         assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
   1226         dt1.roll!"days"(-32);
   1227         assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
   1228 
   1229         auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
   1230         dt2.roll!"hours"(1);
   1231         assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
   1232 
   1233         auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
   1234         dt3.roll!"seconds"(-1);
   1235         assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
   1236     }
   1237 
   1238     @safe unittest
   1239     {
   1240         auto dt = DateTime(2000, 1, 31);
   1241         dt.roll!"days"(7).roll!"days"(-4);
   1242         assert(dt == DateTime(2000, 1, 3));
   1243 
   1244         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1245         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1246         static assert(!__traits(compiles, cdt.roll!"days"(4)));
   1247         static assert(!__traits(compiles, idt.roll!"days"(4)));
   1248     }
   1249 
   1250 
   1251     /// ditto
   1252     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
   1253         if (units == "hours" ||
   1254             units == "minutes" ||
   1255             units == "seconds")
   1256     {
   1257         _tod.roll!units(value);
   1258         return this;
   1259     }
   1260 
   1261     // Test roll!"hours"().
   1262     @safe unittest
   1263     {
   1264         static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
   1265         {
   1266             orig.roll!"hours"(hours);
   1267             assert(orig == expected);
   1268         }
   1269 
   1270         // Test A.D.
   1271         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
   1272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1273         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
   1274                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
   1275         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
   1276                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
   1277         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
   1278                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
   1279         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
   1280                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
   1281         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
   1282                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
   1283         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
   1284                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
   1285         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
   1286                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
   1287         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
   1288                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
   1289         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
   1290                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
   1291         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
   1292                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
   1293         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
   1294                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
   1295         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
   1296                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
   1297         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
   1298                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
   1299         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
   1300                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
   1301         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
   1302                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
   1303         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
   1304                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
   1305         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
   1306                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
   1307         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
   1308                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
   1309         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
   1310                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
   1311         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
   1312                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
   1313         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
   1314                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
   1315         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
   1316                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
   1317         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
   1318                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
   1319         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
   1320                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1321         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
   1322                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
   1323 
   1324         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
   1325                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
   1326         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
   1327                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
   1328         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
   1329                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
   1330         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
   1331                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
   1332         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
   1333                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
   1334         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
   1335                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
   1336         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
   1337                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
   1338         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
   1339                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
   1340         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
   1341                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
   1342         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
   1343                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
   1344         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
   1345                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
   1346         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
   1347                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
   1348         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
   1349                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
   1350         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
   1351                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
   1352         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
   1353                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
   1354         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
   1355                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
   1356         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
   1357                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
   1358         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
   1359                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
   1360         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
   1361                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
   1362         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
   1363                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
   1364         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
   1365                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
   1366         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
   1367                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
   1368         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
   1369                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
   1370         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
   1371                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1372         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
   1373                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
   1374 
   1375         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
   1376                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
   1377         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
   1378                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
   1379         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
   1380                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
   1381 
   1382         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
   1383                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
   1384         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
   1385                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
   1386         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
   1387                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
   1388 
   1389         testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
   1390                DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33)));
   1391         testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
   1392                DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33)));
   1393 
   1394         testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1,
   1395                DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33)));
   1396         testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
   1397                DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33)));
   1398 
   1399         testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25,
   1400                DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33)));
   1401         testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25,
   1402                DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33)));
   1403 
   1404         testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
   1405                DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33)));
   1406         testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
   1407                DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33)));
   1408 
   1409         // Test B.C.
   1410         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
   1411                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1412         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
   1413                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
   1414         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
   1415                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
   1416         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
   1417                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
   1418         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
   1419                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
   1420         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
   1421                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
   1422         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
   1423                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
   1424         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
   1425                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
   1426         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
   1427                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
   1428         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
   1429                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
   1430         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
   1431                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
   1432         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
   1433                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
   1434         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
   1435                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
   1436         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
   1437                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
   1438         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
   1439                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
   1440         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
   1441                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
   1442         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
   1443                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
   1444         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
   1445                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
   1446         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
   1447                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
   1448         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
   1449                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
   1450         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
   1451                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
   1452         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
   1453                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
   1454         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
   1455                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
   1456         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
   1457                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
   1458         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
   1459                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1460         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
   1461                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
   1462 
   1463         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
   1464                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
   1465         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
   1466                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
   1467         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
   1468                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
   1469         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
   1470                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
   1471         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
   1472                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
   1473         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
   1474                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
   1475         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
   1476                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
   1477         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
   1478                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
   1479         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
   1480                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
   1481         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
   1482                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
   1483         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
   1484                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
   1485         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
   1486                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
   1487         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
   1488                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
   1489         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
   1490                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
   1491         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
   1492                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
   1493         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
   1494                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
   1495         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
   1496                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
   1497         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
   1498                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
   1499         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
   1500                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
   1501         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
   1502                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
   1503         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
   1504                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
   1505         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
   1506                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
   1507         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
   1508                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
   1509         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
   1510                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1511         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
   1512                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
   1513 
   1514         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
   1515                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
   1516         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
   1517                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
   1518         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
   1519                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
   1520 
   1521         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
   1522                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
   1523         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
   1524                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
   1525         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
   1526                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
   1527 
   1528         testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
   1529                DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33)));
   1530         testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
   1531                DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33)));
   1532 
   1533         testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1,
   1534                DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33)));
   1535         testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
   1536                DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33)));
   1537 
   1538         testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25,
   1539                DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33)));
   1540         testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25,
   1541                DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33)));
   1542 
   1543         testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
   1544                DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33)));
   1545         testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
   1546                DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33)));
   1547 
   1548         // Test Both
   1549         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546,
   1550                DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33)));
   1551         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546,
   1552                DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33)));
   1553 
   1554         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
   1555         dt.roll!"hours"(27).roll!"hours"(-9);
   1556         assert(dt == DateTime(2000, 1, 31, 3, 7, 6));
   1557 
   1558         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1559         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1560         static assert(!__traits(compiles, cdt.roll!"hours"(4)));
   1561         static assert(!__traits(compiles, idt.roll!"hours"(4)));
   1562     }
   1563 
   1564     // Test roll!"minutes"().
   1565     @safe unittest
   1566     {
   1567         static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
   1568         {
   1569             orig.roll!"minutes"(minutes);
   1570             assert(orig == expected);
   1571         }
   1572 
   1573         // Test A.D.
   1574         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
   1575                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1576         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
   1577                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
   1578         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
   1579                DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33)));
   1580         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
   1581                DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33)));
   1582         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
   1583                DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33)));
   1584         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
   1585                DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33)));
   1586         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
   1587                DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33)));
   1588         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
   1589                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
   1590         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
   1591                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
   1592         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
   1593                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1594         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
   1595                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
   1596         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
   1597                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1598         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
   1599                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
   1600         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
   1601                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1602         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
   1603                DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33)));
   1604 
   1605         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
   1606                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
   1607         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
   1608                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1609         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
   1610                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
   1611         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
   1612                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1613         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
   1614                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
   1615         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
   1616                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1617         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
   1618                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
   1619         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
   1620                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1621 
   1622         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
   1623                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
   1624         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
   1625                DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33)));
   1626         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
   1627                DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33)));
   1628         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
   1629                DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33)));
   1630         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
   1631                DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33)));
   1632         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
   1633                DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33)));
   1634         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
   1635                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
   1636         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
   1637                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
   1638         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
   1639                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1640         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
   1641                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
   1642         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
   1643                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1644         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
   1645                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
   1646         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
   1647                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1648         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
   1649                DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33)));
   1650 
   1651         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
   1652                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
   1653         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
   1654                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1655         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
   1656                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
   1657         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
   1658                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1659         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
   1660                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
   1661         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
   1662                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1663         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
   1664                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
   1665         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
   1666                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1667 
   1668         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
   1669                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
   1670         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
   1671                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
   1672         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
   1673                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
   1674 
   1675         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
   1676                DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33)));
   1677         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
   1678                DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)));
   1679         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
   1680                DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33)));
   1681 
   1682         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
   1683                DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33)));
   1684         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
   1685                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)));
   1686         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
   1687                DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33)));
   1688 
   1689         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
   1690                DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33)));
   1691         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
   1692                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)));
   1693         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
   1694                DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33)));
   1695 
   1696         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1,
   1697                DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33)));
   1698         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0,
   1699                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)));
   1700         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1,
   1701                DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33)));
   1702 
   1703         // Test B.C.
   1704         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
   1705                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1706         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
   1707                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
   1708         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
   1709                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33)));
   1710         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
   1711                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33)));
   1712         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
   1713                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33)));
   1714         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
   1715                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33)));
   1716         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
   1717                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33)));
   1718         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
   1719                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
   1720         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
   1721                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
   1722         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
   1723                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1724         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
   1725                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
   1726         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
   1727                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1728         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
   1729                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
   1730         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
   1731                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1732         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
   1733                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33)));
   1734 
   1735         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
   1736                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
   1737         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
   1738                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1739         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
   1740                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
   1741         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
   1742                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1743         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
   1744                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
   1745         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
   1746                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1747         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
   1748                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
   1749         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
   1750                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1751 
   1752         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
   1753                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
   1754         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
   1755                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33)));
   1756         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
   1757                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33)));
   1758         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
   1759                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33)));
   1760         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
   1761                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33)));
   1762         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
   1763                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33)));
   1764         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
   1765                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
   1766         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
   1767                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
   1768         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
   1769                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1770         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
   1771                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
   1772         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
   1773                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1774         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
   1775                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
   1776         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
   1777                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1778         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
   1779                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33)));
   1780 
   1781         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
   1782                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
   1783         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
   1784                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1785         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
   1786                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
   1787         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
   1788                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1789         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
   1790                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
   1791         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
   1792                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1793         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
   1794                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
   1795         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
   1796                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1797 
   1798         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
   1799                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
   1800         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
   1801                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
   1802         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
   1803                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
   1804 
   1805         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
   1806                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33)));
   1807         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
   1808                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)));
   1809         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
   1810                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33)));
   1811 
   1812         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
   1813                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33)));
   1814         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
   1815                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)));
   1816         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
   1817                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33)));
   1818 
   1819         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
   1820                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33)));
   1821         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
   1822                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)));
   1823         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
   1824                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33)));
   1825 
   1826         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1,
   1827                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33)));
   1828         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0,
   1829                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)));
   1830         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1,
   1831                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33)));
   1832 
   1833         // Test Both
   1834         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
   1835                DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0)));
   1836         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1,
   1837                DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0)));
   1838 
   1839         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
   1840                DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0)));
   1841         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1,
   1842                DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0)));
   1843 
   1844         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760,
   1845                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
   1846         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760,
   1847                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
   1848 
   1849         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782,
   1850                DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33)));
   1851         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782,
   1852                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
   1853 
   1854         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
   1855         dt.roll!"minutes"(92).roll!"minutes"(-292);
   1856         assert(dt == DateTime(2000, 1, 31, 9, 47, 6));
   1857 
   1858         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   1859         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   1860         static assert(!__traits(compiles, cdt.roll!"minutes"(4)));
   1861         static assert(!__traits(compiles, idt.roll!"minutes"(4)));
   1862     }
   1863 
   1864     // Test roll!"seconds"().
   1865     @safe unittest
   1866     {
   1867         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
   1868         {
   1869             orig.roll!"seconds"(seconds);
   1870             assert(orig == expected);
   1871         }
   1872 
   1873         // Test A.D.
   1874         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
   1875                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1876         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
   1877                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
   1878         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
   1879                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35)));
   1880         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
   1881                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36)));
   1882         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
   1883                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37)));
   1884         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
   1885                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38)));
   1886         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
   1887                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43)));
   1888         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
   1889                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48)));
   1890         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
   1891                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
   1892         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
   1893                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
   1894         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
   1895                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3)));
   1896         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
   1897                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
   1898         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
   1899                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1900         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
   1901                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
   1902 
   1903         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
   1904                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
   1905         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
   1906                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
   1907         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
   1908                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
   1909         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
   1910                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
   1911         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
   1912                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
   1913         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
   1914                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1915         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
   1916                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
   1917         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
   1918                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1919 
   1920         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
   1921                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
   1922         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
   1923                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31)));
   1924         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
   1925                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30)));
   1926         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
   1927                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29)));
   1928         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
   1929                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28)));
   1930         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
   1931                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23)));
   1932         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
   1933                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18)));
   1934         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
   1935                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
   1936         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
   1937                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
   1938         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
   1939                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58)));
   1940         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
   1941                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
   1942         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
   1943                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   1944         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
   1945                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
   1946 
   1947         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
   1948                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
   1949         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
   1950                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
   1951         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
   1952                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
   1953 
   1954         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
   1955                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1)));
   1956         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
   1957                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)));
   1958         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
   1959                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59)));
   1960 
   1961         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
   1962                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1)));
   1963         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
   1964                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)));
   1965         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
   1966                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59)));
   1967 
   1968         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
   1969                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0)));
   1970         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
   1971                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)));
   1972         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
   1973                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58)));
   1974 
   1975         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1,
   1976                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0)));
   1977         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0,
   1978                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)));
   1979         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1,
   1980                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58)));
   1981 
   1982         // Test B.C.
   1983         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
   1984                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   1985         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
   1986                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
   1987         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
   1988                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35)));
   1989         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
   1990                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36)));
   1991         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
   1992                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37)));
   1993         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
   1994                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38)));
   1995         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
   1996                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43)));
   1997         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
   1998                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48)));
   1999         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
   2000                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
   2001         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
   2002                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
   2003         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
   2004                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3)));
   2005         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
   2006                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
   2007         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
   2008                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   2009         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
   2010                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
   2011 
   2012         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
   2013                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
   2014         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
   2015                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
   2016         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
   2017                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
   2018         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
   2019                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
   2020         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
   2021                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
   2022         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
   2023                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   2024         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
   2025                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
   2026         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
   2027                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   2028 
   2029         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
   2030                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
   2031         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
   2032                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31)));
   2033         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
   2034                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30)));
   2035         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
   2036                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29)));
   2037         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
   2038                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28)));
   2039         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
   2040                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23)));
   2041         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
   2042                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18)));
   2043         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
   2044                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
   2045         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
   2046                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
   2047         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
   2048                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58)));
   2049         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
   2050                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
   2051         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
   2052                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   2053         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
   2054                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
   2055 
   2056         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
   2057                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
   2058         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
   2059                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
   2060         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
   2061                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
   2062 
   2063         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
   2064                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1)));
   2065         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
   2066                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)));
   2067         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
   2068                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59)));
   2069 
   2070         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
   2071                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1)));
   2072         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
   2073                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)));
   2074         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
   2075                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59)));
   2076 
   2077         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
   2078                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0)));
   2079         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
   2080                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)));
   2081         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
   2082                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58)));
   2083 
   2084         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1,
   2085                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0)));
   2086         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0,
   2087                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)));
   2088         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1,
   2089                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58)));
   2090 
   2091         // Test Both
   2092         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
   2093                DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59)));
   2094         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1,
   2095                DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)));
   2096 
   2097         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
   2098                DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59)));
   2099         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1,
   2100                DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)));
   2101 
   2102         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L,
   2103                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
   2104         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L,
   2105                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
   2106 
   2107         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L,
   2108                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50)));
   2109         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L,
   2110                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
   2111 
   2112         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
   2113         dt.roll!"seconds"(92).roll!"seconds"(-292);
   2114         assert(dt == DateTime(2000, 1, 31, 9, 7, 46));
   2115 
   2116         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   2117         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   2118         static assert(!__traits(compiles, cdt.roll!"seconds"(4)));
   2119         static assert(!__traits(compiles, idt.roll!"seconds"(4)));
   2120     }
   2121 
   2122 
   2123     import core.time : Duration;
   2124     /++
   2125         Gives the result of adding or subtracting a $(REF Duration, core,time)
   2126         from this $(LREF DateTime).
   2127 
   2128         The legal types of arithmetic for $(LREF DateTime) using this operator
   2129         are
   2130 
   2131         $(BOOKTABLE,
   2132         $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
   2133         $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
   2134         )
   2135 
   2136         Params:
   2137             duration = The $(REF Duration, core,time) to add to or subtract from
   2138                        this $(LREF DateTime).
   2139       +/
   2140     DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
   2141         if (op == "+" || op == "-")
   2142     {
   2143         DateTime retval = this;
   2144         immutable seconds = duration.total!"seconds";
   2145         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
   2146     }
   2147 
   2148     ///
   2149     @safe unittest
   2150     {
   2151         import core.time : hours, seconds;
   2152 
   2153         assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
   2154                DateTime(2016, 1, 1, 0, 0, 0));
   2155 
   2156         assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
   2157                DateTime(2016, 1, 1, 0, 59, 59));
   2158 
   2159         assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
   2160                DateTime(2015, 12, 31, 23, 59, 59));
   2161 
   2162         assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
   2163                DateTime(2015, 12, 31, 23, 59, 59));
   2164     }
   2165 
   2166     @safe unittest
   2167     {
   2168         import core.time : dur;
   2169 
   2170         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2171 
   2172         assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
   2173         assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
   2174         assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
   2175         assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
   2176 
   2177         assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
   2178         assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
   2179         assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
   2180         assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
   2181         assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2182         assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2183         assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2184         assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2185         assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2186         assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2187         assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2188         assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2189 
   2190         assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
   2191         assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
   2192         assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
   2193         assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
   2194 
   2195         assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
   2196         assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
   2197         assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
   2198         assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
   2199         assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2200         assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2201         assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2202         assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2203         assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2204         assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2205         assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2206         assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2207 
   2208         auto duration = dur!"seconds"(12);
   2209         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2210         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2211         assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45));
   2212         assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45));
   2213         assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21));
   2214         assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21));
   2215     }
   2216 
   2217 
   2218     /++
   2219         Gives the result of adding or subtracting a duration from this
   2220         $(LREF DateTime), as well as assigning the result to this
   2221         $(LREF DateTime).
   2222 
   2223         The legal types of arithmetic for $(LREF DateTime) using this operator
   2224         are
   2225 
   2226         $(BOOKTABLE,
   2227         $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime))
   2228         $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime))
   2229         )
   2230 
   2231         Params:
   2232             duration = The duration to add to or subtract from this
   2233                        $(LREF DateTime).
   2234       +/
   2235     ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
   2236         if (op == "+" || op == "-")
   2237     {
   2238         import core.time : convert;
   2239         import std.format : format;
   2240 
   2241         DateTime retval = this;
   2242         immutable hnsecs = duration.total!"hnsecs";
   2243 
   2244         mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
   2245     }
   2246 
   2247     @safe unittest
   2248     {
   2249         import core.time : dur;
   2250         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
   2251                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
   2252         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
   2253                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
   2254         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) ==
   2255                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
   2256         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) ==
   2257                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
   2258 
   2259         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) ==
   2260                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
   2261         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) ==
   2262                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
   2263         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) ==
   2264                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
   2265         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) ==
   2266                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
   2267         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) ==
   2268                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2269         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) ==
   2270                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2271         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) ==
   2272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2273         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) ==
   2274                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2275         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) ==
   2276                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2277         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) ==
   2278                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2279         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) ==
   2280                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2281         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) ==
   2282                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2283 
   2284         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) ==
   2285                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
   2286         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) ==
   2287                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
   2288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) ==
   2289                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
   2290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) ==
   2291                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
   2292 
   2293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) ==
   2294                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
   2295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) ==
   2296                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
   2297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) ==
   2298                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
   2299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) ==
   2300                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
   2301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) ==
   2302                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) ==
   2304                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2305         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) ==
   2306                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2307         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) ==
   2308                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2309         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) ==
   2310                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2311         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) ==
   2312                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2313         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) ==
   2314                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
   2315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) ==
   2316                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
   2317 
   2318         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
   2319         (dt += dur!"seconds"(92)) -= dur!"days"(-500);
   2320         assert(dt == DateTime(2001, 6, 14, 9, 8, 38));
   2321 
   2322         auto duration = dur!"seconds"(12);
   2323         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2324         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2325         static assert(!__traits(compiles, cdt += duration));
   2326         static assert(!__traits(compiles, idt += duration));
   2327         static assert(!__traits(compiles, cdt -= duration));
   2328         static assert(!__traits(compiles, idt -= duration));
   2329     }
   2330 
   2331 
   2332     /++
   2333         Gives the difference between two $(LREF DateTime)s.
   2334 
   2335         The legal types of arithmetic for $(LREF DateTime) using this operator are
   2336 
   2337         $(BOOKTABLE,
   2338         $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
   2339         )
   2340       +/
   2341     Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
   2342         if (op == "-")
   2343     {
   2344         immutable dateResult = _date - rhs.date;
   2345         immutable todResult = _tod - rhs._tod;
   2346 
   2347         import core.time : dur;
   2348         return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
   2349     }
   2350 
   2351     @safe unittest
   2352     {
   2353         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
   2354 
   2355         import core.time : dur;
   2356         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
   2357                dur!"seconds"(31_536_000));
   2358         assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
   2359                dur!"seconds"(-31_536_000));
   2360 
   2361         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
   2362                dur!"seconds"(26_78_400));
   2363         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) ==
   2364                dur!"seconds"(-26_78_400));
   2365 
   2366         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) ==
   2367                dur!"seconds"(86_400));
   2368         assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
   2369                dur!"seconds"(-86_400));
   2370 
   2371         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) ==
   2372                dur!"seconds"(3600));
   2373         assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
   2374                dur!"seconds"(-3600));
   2375 
   2376         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
   2377                dur!"seconds"(60));
   2378         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) ==
   2379                dur!"seconds"(-60));
   2380 
   2381         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
   2382                dur!"seconds"(1));
   2383         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) ==
   2384                dur!"seconds"(-1));
   2385 
   2386         assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033));
   2387         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033));
   2388         assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367));
   2389         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367));
   2390 
   2391         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2392         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2393         assert(dt - dt == Duration.zero);
   2394         assert(cdt - dt == Duration.zero);
   2395         assert(idt - dt == Duration.zero);
   2396 
   2397         assert(dt - cdt == Duration.zero);
   2398         assert(cdt - cdt == Duration.zero);
   2399         assert(idt - cdt == Duration.zero);
   2400 
   2401         assert(dt - idt == Duration.zero);
   2402         assert(cdt - idt == Duration.zero);
   2403         assert(idt - idt == Duration.zero);
   2404     }
   2405 
   2406 
   2407     /++
   2408         Returns the difference between the two $(LREF DateTime)s in months.
   2409 
   2410         To get the difference in years, subtract the year property
   2411         of two $(LREF DateTime)s. To get the difference in days or weeks,
   2412         subtract the $(LREF DateTime)s themselves and use the
   2413         $(REF Duration, core,time) that results. Because converting between
   2414         months and smaller units requires a specific date (which
   2415         $(REF Duration, core,time)s don't have), getting the difference in
   2416         months requires some math using both the year and month properties, so
   2417         this is a convenience function for getting the difference in months.
   2418 
   2419         Note that the number of days in the months or how far into the month
   2420         either date is is irrelevant. It is the difference in the month property
   2421         combined with the difference in years * 12. So, for instance,
   2422         December 31st and January 1st are one month apart just as December 1st
   2423         and January 31st are one month apart.
   2424 
   2425         Params:
   2426             rhs = The $(LREF DateTime) to subtract from this one.
   2427       +/
   2428     int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
   2429     {
   2430         return _date.diffMonths(rhs._date);
   2431     }
   2432 
   2433     ///
   2434     @safe unittest
   2435     {
   2436         assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
   2437                    DateTime(1999, 1, 31, 23, 59, 59)) == 1);
   2438 
   2439         assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
   2440                    DateTime(1999, 2, 1, 12, 3, 42)) == -1);
   2441 
   2442         assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
   2443                    DateTime(1999, 1, 1, 2, 4, 7)) == 2);
   2444 
   2445         assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
   2446                    DateTime(1999, 3, 31, 0, 30, 58)) == -2);
   2447     }
   2448 
   2449     @safe unittest
   2450     {
   2451         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2452         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2453         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2454         assert(dt.diffMonths(dt) == 0);
   2455         assert(cdt.diffMonths(dt) == 0);
   2456         assert(idt.diffMonths(dt) == 0);
   2457 
   2458         assert(dt.diffMonths(cdt) == 0);
   2459         assert(cdt.diffMonths(cdt) == 0);
   2460         assert(idt.diffMonths(cdt) == 0);
   2461 
   2462         assert(dt.diffMonths(idt) == 0);
   2463         assert(cdt.diffMonths(idt) == 0);
   2464         assert(idt.diffMonths(idt) == 0);
   2465     }
   2466 
   2467 
   2468     /++
   2469         Whether this $(LREF DateTime) is in a leap year.
   2470      +/
   2471     @property bool isLeapYear() const @safe pure nothrow @nogc
   2472     {
   2473         return _date.isLeapYear;
   2474     }
   2475 
   2476     @safe unittest
   2477     {
   2478         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2479         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2480         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2481         assert(!dt.isLeapYear);
   2482         assert(!cdt.isLeapYear);
   2483         assert(!idt.isLeapYear);
   2484     }
   2485 
   2486 
   2487     /++
   2488         Day of the week this $(LREF DateTime) is on.
   2489       +/
   2490     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
   2491     {
   2492         return _date.dayOfWeek;
   2493     }
   2494 
   2495     @safe unittest
   2496     {
   2497         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2498         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2499         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2500         assert(dt.dayOfWeek == DayOfWeek.tue);
   2501         assert(cdt.dayOfWeek == DayOfWeek.tue);
   2502         assert(idt.dayOfWeek == DayOfWeek.tue);
   2503     }
   2504 
   2505 
   2506     /++
   2507         Day of the year this $(LREF DateTime) is on.
   2508       +/
   2509     @property ushort dayOfYear() const @safe pure nothrow @nogc
   2510     {
   2511         return _date.dayOfYear;
   2512     }
   2513 
   2514     ///
   2515     @safe unittest
   2516     {
   2517         assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
   2518         assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
   2519         assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
   2520     }
   2521 
   2522     @safe unittest
   2523     {
   2524         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2525         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2526         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2527         assert(dt.dayOfYear == 187);
   2528         assert(cdt.dayOfYear == 187);
   2529         assert(idt.dayOfYear == 187);
   2530     }
   2531 
   2532 
   2533     /++
   2534         Day of the year.
   2535 
   2536         Params:
   2537             day = The day of the year to set which day of the year this
   2538                   $(LREF DateTime) is on.
   2539       +/
   2540     @property void dayOfYear(int day) @safe pure
   2541     {
   2542         _date.dayOfYear = day;
   2543     }
   2544 
   2545     @safe unittest
   2546     {
   2547         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2548         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2549         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2550         dt.dayOfYear = 12;
   2551         assert(dt.dayOfYear == 12);
   2552         static assert(!__traits(compiles, cdt.dayOfYear = 12));
   2553         static assert(!__traits(compiles, idt.dayOfYear = 12));
   2554     }
   2555 
   2556 
   2557     /++
   2558         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
   2559      +/
   2560     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
   2561     {
   2562         return _date.dayOfGregorianCal;
   2563     }
   2564 
   2565     ///
   2566     @safe unittest
   2567     {
   2568         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
   2569         assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
   2570         assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
   2571 
   2572         assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
   2573         assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
   2574         assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
   2575 
   2576         assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
   2577         assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
   2578     }
   2579 
   2580     @safe unittest
   2581     {
   2582         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2583         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2584         assert(cdt.dayOfGregorianCal == 729_941);
   2585         assert(idt.dayOfGregorianCal == 729_941);
   2586     }
   2587 
   2588 
   2589     /++
   2590         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
   2591         Setting this property does not affect the time portion of
   2592         $(LREF DateTime).
   2593 
   2594         Params:
   2595             days = The day of the Gregorian Calendar to set this $(LREF DateTime)
   2596                    to.
   2597      +/
   2598     @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
   2599     {
   2600         _date.dayOfGregorianCal = days;
   2601     }
   2602 
   2603     ///
   2604     @safe unittest
   2605     {
   2606         auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
   2607         dt.dayOfGregorianCal = 1;
   2608         assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
   2609 
   2610         dt.dayOfGregorianCal = 365;
   2611         assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
   2612 
   2613         dt.dayOfGregorianCal = 366;
   2614         assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
   2615 
   2616         dt.dayOfGregorianCal = 0;
   2617         assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
   2618 
   2619         dt.dayOfGregorianCal = -365;
   2620         assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
   2621 
   2622         dt.dayOfGregorianCal = -366;
   2623         assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
   2624 
   2625         dt.dayOfGregorianCal = 730_120;
   2626         assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
   2627 
   2628         dt.dayOfGregorianCal = 734_137;
   2629         assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
   2630     }
   2631 
   2632     @safe unittest
   2633     {
   2634         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2635         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2636         static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7));
   2637         static assert(!__traits(compiles, idt.dayOfGregorianCal = 7));
   2638     }
   2639 
   2640 
   2641     /++
   2642         The ISO 8601 week of the year that this $(LREF DateTime) is in.
   2643 
   2644         See_Also:
   2645             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
   2646       +/
   2647     @property ubyte isoWeek() const @safe pure nothrow
   2648     {
   2649         return _date.isoWeek;
   2650     }
   2651 
   2652     @safe unittest
   2653     {
   2654         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2655         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2656         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2657         assert(dt.isoWeek == 27);
   2658         assert(cdt.isoWeek == 27);
   2659         assert(idt.isoWeek == 27);
   2660     }
   2661 
   2662 
   2663     /++
   2664         The year of the ISO 8601 week calendar that this $(LREF DateTime) is in.
   2665 
   2666         See_Also:
   2667             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
   2668       +/
   2669     @property short isoWeekYear() const @safe pure nothrow
   2670     {
   2671         return _date.isoWeekYear;
   2672     }
   2673 
   2674 
   2675     /++
   2676         $(LREF DateTime) for the last day in the month that this
   2677         $(LREF DateTime) is in. The time portion of endOfMonth is always
   2678         23:59:59.
   2679       +/
   2680     @property DateTime endOfMonth() const @safe pure nothrow
   2681     {
   2682         try
   2683             return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
   2684         catch (Exception e)
   2685             assert(0, "DateTime constructor threw.");
   2686     }
   2687 
   2688     ///
   2689     @safe unittest
   2690     {
   2691         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
   2692                DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
   2693 
   2694         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
   2695                DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
   2696 
   2697         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
   2698                DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
   2699 
   2700         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
   2701                DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
   2702     }
   2703 
   2704     @safe unittest
   2705     {
   2706         // Test A.D.
   2707         assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59));
   2708         assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59));
   2709         assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59));
   2710         assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59));
   2711         assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59));
   2712         assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59));
   2713         assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59));
   2714         assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
   2715         assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59));
   2716         assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59));
   2717         assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59));
   2718         assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59));
   2719         assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59));
   2720 
   2721         // Test B.C.
   2722         assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59));
   2723         assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59));
   2724         assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59));
   2725         assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59));
   2726         assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59));
   2727         assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59));
   2728         assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59));
   2729         assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59));
   2730         assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59));
   2731         assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59));
   2732         assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59));
   2733         assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59));
   2734         assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59));
   2735 
   2736         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2737         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2738         assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
   2739         assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
   2740     }
   2741 
   2742 
   2743     /++
   2744         The last day in the month that this $(LREF DateTime) is in.
   2745       +/
   2746     @property ubyte daysInMonth() const @safe pure nothrow @nogc
   2747     {
   2748         return _date.daysInMonth;
   2749     }
   2750 
   2751     ///
   2752     @safe unittest
   2753     {
   2754         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
   2755         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
   2756         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
   2757         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
   2758     }
   2759 
   2760     @safe unittest
   2761     {
   2762         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2763         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2764         assert(cdt.daysInMonth == 31);
   2765         assert(idt.daysInMonth == 31);
   2766     }
   2767 
   2768 
   2769     /++
   2770         Whether the current year is a date in A.D.
   2771       +/
   2772     @property bool isAD() const @safe pure nothrow @nogc
   2773     {
   2774         return _date.isAD;
   2775     }
   2776 
   2777     ///
   2778     @safe unittest
   2779     {
   2780         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
   2781         assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
   2782         assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
   2783         assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
   2784     }
   2785 
   2786     @safe unittest
   2787     {
   2788         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2789         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2790         assert(cdt.isAD);
   2791         assert(idt.isAD);
   2792     }
   2793 
   2794 
   2795     /++
   2796         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
   2797         $(LREF DateTime) at the given time. For example, prior to noon,
   2798         1996-03-31 would be the Julian day number 2_450_173, so this function
   2799         returns 2_450_173, while from noon onward, the julian day number would
   2800         be 2_450_174, so this function returns 2_450_174.
   2801       +/
   2802     @property long julianDay() const @safe pure nothrow @nogc
   2803     {
   2804         if (_tod._hour < 12)
   2805             return _date.julianDay - 1;
   2806         else
   2807             return _date.julianDay;
   2808     }
   2809 
   2810     @safe unittest
   2811     {
   2812         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1);
   2813         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0);
   2814 
   2815         assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424);
   2816         assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425);
   2817 
   2818         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425);
   2819         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426);
   2820 
   2821         assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160);
   2822         assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161);
   2823 
   2824         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000);
   2825         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001);
   2826 
   2827         assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973);
   2828         assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974);
   2829 
   2830         assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173);
   2831         assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174);
   2832 
   2833         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432);
   2834         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433);
   2835 
   2836         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2837         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2838         assert(cdt.julianDay == 2_451_366);
   2839         assert(idt.julianDay == 2_451_366);
   2840     }
   2841 
   2842 
   2843     /++
   2844         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
   2845         time on this date (since, the modified Julian day changes at midnight).
   2846       +/
   2847     @property long modJulianDay() const @safe pure nothrow @nogc
   2848     {
   2849         return _date.modJulianDay;
   2850     }
   2851 
   2852     @safe unittest
   2853     {
   2854         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0);
   2855         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0);
   2856 
   2857         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432);
   2858         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432);
   2859 
   2860         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2861         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   2862         assert(cdt.modJulianDay == 51_365);
   2863         assert(idt.modJulianDay == 51_365);
   2864     }
   2865 
   2866 
   2867     /++
   2868         Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`.
   2869         If `writer` is set, the resulting string will be written directly to it.
   2870 
   2871         Params:
   2872             writer = A `char` accepting
   2873             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   2874         Returns:
   2875             A `string` when not using an output range; `void` otherwise.
   2876       +/
   2877     string toISOString() const @safe pure nothrow
   2878     {
   2879         import std.array : appender;
   2880         auto w = appender!string();
   2881         w.reserve(18);
   2882         try
   2883             toISOString(w);
   2884         catch (Exception e)
   2885             assert(0, "toISOString() threw.");
   2886         return w.data;
   2887     }
   2888 
   2889     /// ditto
   2890     void toISOString(W)(ref W writer) const
   2891     if (isOutputRange!(W, char))
   2892     {
   2893         import std.format.write : formattedWrite;
   2894         _date.toISOString(writer);
   2895         formattedWrite!("T%02d%02d%02d")(
   2896             writer,
   2897             _tod._hour,
   2898             _tod._minute,
   2899             _tod._second
   2900         );
   2901     }
   2902 
   2903     ///
   2904     @safe unittest
   2905     {
   2906         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
   2907                "20100704T070612");
   2908 
   2909         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
   2910                "19981225T021500");
   2911 
   2912         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
   2913                "00000105T230959");
   2914 
   2915         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
   2916                "-00040105T000002");
   2917     }
   2918 
   2919     @safe unittest
   2920     {
   2921         // Test A.D.
   2922         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000");
   2923         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612");
   2924         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459");
   2925         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959");
   2926         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101");
   2927 
   2928         // Test B.C.
   2929         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204");
   2930         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000");
   2931         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612");
   2932         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459");
   2933         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959");
   2934         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101");
   2935 
   2936         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   2937         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   2938         assert(cdt.toISOString() == "19990706T123033");
   2939         assert(idt.toISOString() == "19990706T123033");
   2940     }
   2941 
   2942 
   2943     /++
   2944         Converts this $(LREF DateTime) to a string with the format
   2945         `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting
   2946         string will be written directly to it.
   2947 
   2948         Params:
   2949             writer = A `char` accepting
   2950             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   2951         Returns:
   2952             A `string` when not using an output range; `void` otherwise.
   2953       +/
   2954     string toISOExtString() const @safe pure nothrow
   2955     {
   2956         import std.array : appender;
   2957         auto w = appender!string();
   2958         w.reserve(20);
   2959         try
   2960             toISOExtString(w);
   2961         catch (Exception e)
   2962             assert(0, "toISOExtString() threw.");
   2963         return w.data;
   2964     }
   2965 
   2966     /// ditto
   2967     void toISOExtString(W)(ref W writer) const
   2968     if (isOutputRange!(W, char))
   2969     {
   2970         import std.format.write : formattedWrite;
   2971         _date.toISOExtString(writer);
   2972         formattedWrite!("T%02d:%02d:%02d")(
   2973             writer,
   2974             _tod._hour,
   2975             _tod._minute,
   2976             _tod._second
   2977         );
   2978     }
   2979 
   2980     ///
   2981     @safe unittest
   2982     {
   2983         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
   2984                "2010-07-04T07:06:12");
   2985 
   2986         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
   2987                "1998-12-25T02:15:00");
   2988 
   2989         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
   2990                "0000-01-05T23:09:59");
   2991 
   2992         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
   2993                "-0004-01-05T00:00:02");
   2994     }
   2995 
   2996     @safe unittest
   2997     {
   2998         // Test A.D.
   2999         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00");
   3000         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12");
   3001         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59");
   3002         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59");
   3003         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01");
   3004 
   3005         // Test B.C.
   3006         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04");
   3007         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00");
   3008         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12");
   3009         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59");
   3010         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59");
   3011         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01");
   3012 
   3013         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   3014         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   3015         assert(cdt.toISOExtString() == "1999-07-06T12:30:33");
   3016         assert(idt.toISOExtString() == "1999-07-06T12:30:33");
   3017     }
   3018 
   3019     /++
   3020         Converts this $(LREF DateTime) to a string with the format
   3021         `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting
   3022         string will be written directly to it.
   3023 
   3024         Params:
   3025             writer = A `char` accepting
   3026             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   3027         Returns:
   3028             A `string` when not using an output range; `void` otherwise.
   3029       +/
   3030     string toSimpleString() const @safe pure nothrow
   3031     {
   3032         import std.array : appender;
   3033         auto w = appender!string();
   3034         w.reserve(22);
   3035         try
   3036             toSimpleString(w);
   3037         catch (Exception e)
   3038             assert(0, "toSimpleString() threw.");
   3039         return w.data;
   3040     }
   3041 
   3042     /// ditto
   3043     void toSimpleString(W)(ref W writer) const
   3044     if (isOutputRange!(W, char))
   3045     {
   3046         import std.format.write : formattedWrite;
   3047         _date.toSimpleString(writer);
   3048         formattedWrite!(" %02d:%02d:%02d")(
   3049             writer,
   3050             _tod._hour,
   3051             _tod._minute,
   3052             _tod._second
   3053         );
   3054     }
   3055 
   3056     ///
   3057     @safe unittest
   3058     {
   3059         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
   3060                "2010-Jul-04 07:06:12");
   3061 
   3062         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
   3063                "1998-Dec-25 02:15:00");
   3064 
   3065         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
   3066                "0000-Jan-05 23:09:59");
   3067 
   3068         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
   3069                "-0004-Jan-05 00:00:02");
   3070     }
   3071 
   3072     @safe unittest
   3073     {
   3074         // Test A.D.
   3075         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00");
   3076         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12");
   3077         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59");
   3078         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59");
   3079         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01");
   3080 
   3081         // Test B.C.
   3082         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04");
   3083         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00");
   3084         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12");
   3085         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59");
   3086         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59");
   3087         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01");
   3088 
   3089         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   3090         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   3091         assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33");
   3092         assert(idt.toSimpleString() == "1999-Jul-06 12:30:33");
   3093     }
   3094 
   3095 
   3096     /++
   3097         Converts this $(LREF DateTime) to a string.
   3098 
   3099         This function exists to make it easy to convert a $(LREF DateTime) to a
   3100         string for code that does not care what the exact format is - just that
   3101         it presents the information in a clear manner. It also makes it easy to
   3102         simply convert a $(LREF DateTime) to a string when using functions such
   3103         as `to!string`, `format`, or `writeln` which use toString to convert
   3104         user-defined types. So, it is unlikely that much code will call
   3105         toString directly.
   3106 
   3107         The format of the string is purposefully unspecified, and code that
   3108         cares about the format of the string should use `toISOString`,
   3109         `toISOExtString`, `toSimpleString`, or some other custom formatting
   3110         function that explicitly generates the format that the code needs. The
   3111         reason is that the code is then clear about what format it's using,
   3112         making it less error-prone to maintain the code and interact with other
   3113         software that consumes the generated strings. It's for this same reason
   3114         that $(LREF DateTime) has no `fromString` function, whereas it does have
   3115         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
   3116 
   3117         The format returned by toString may or may not change in the future.
   3118       +/
   3119     string toString() const @safe pure nothrow
   3120     {
   3121         return toSimpleString();
   3122     }
   3123 
   3124     @safe unittest
   3125     {
   3126         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   3127         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   3128         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
   3129         assert(dt.toString());
   3130         assert(cdt.toString());
   3131         assert(idt.toString());
   3132     }
   3133 
   3134     /// ditto
   3135     void toString(W)(ref W writer) const
   3136     if (isOutputRange!(W, char))
   3137     {
   3138         toSimpleString(writer);
   3139     }
   3140 
   3141     /++
   3142         Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
   3143         Whitespace is stripped from the given string.
   3144 
   3145         Params:
   3146             isoString = A string formatted in the ISO format for dates and times.
   3147 
   3148         Throws:
   3149             $(REF DateTimeException,std,datetime,date) if the given string is
   3150             not in the ISO format or if the resulting $(LREF DateTime) would not
   3151             be valid.
   3152       +/
   3153     static DateTime fromISOString(S)(scope const S isoString) @safe pure
   3154         if (isSomeString!S)
   3155     {
   3156         import std.algorithm.searching : countUntil;
   3157         import std.exception : enforce;
   3158         import std.format : format;
   3159         import std.string : strip;
   3160         import std.utf : byCodeUnit;
   3161 
   3162         auto str = strip(isoString);
   3163 
   3164         enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
   3165         auto t = str.byCodeUnit.countUntil('T');
   3166 
   3167         enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
   3168 
   3169         immutable date = Date.fromISOString(str[0 .. t]);
   3170         immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
   3171 
   3172         return DateTime(date, tod);
   3173     }
   3174 
   3175     ///
   3176     @safe unittest
   3177     {
   3178         assert(DateTime.fromISOString("20100704T070612") ==
   3179                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
   3180 
   3181         assert(DateTime.fromISOString("19981225T021500") ==
   3182                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
   3183 
   3184         assert(DateTime.fromISOString("00000105T230959") ==
   3185                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
   3186 
   3187         assert(DateTime.fromISOString("-00040105T000002") ==
   3188                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
   3189 
   3190         assert(DateTime.fromISOString(" 20100704T070612 ") ==
   3191                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
   3192     }
   3193 
   3194     @safe unittest
   3195     {
   3196         assertThrown!DateTimeException(DateTime.fromISOString(""));
   3197         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
   3198         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
   3199         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
   3200         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
   3201         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
   3202 
   3203         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
   3204         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
   3205         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
   3206         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
   3207         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
   3208 
   3209         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
   3210         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
   3211         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
   3212         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
   3213         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
   3214         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
   3215 
   3216         assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
   3217         assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
   3218 
   3219         assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
   3220         assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3221         assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   3222         assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3223         assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3224         assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3225         assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3226     }
   3227 
   3228     // https://issues.dlang.org/show_bug.cgi?id=17801
   3229     @safe unittest
   3230     {
   3231         import std.conv : to;
   3232         import std.meta : AliasSeq;
   3233         static foreach (C; AliasSeq!(char, wchar, dchar))
   3234         {
   3235             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   3236                 assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
   3237         }
   3238     }
   3239 
   3240 
   3241     /++
   3242         Creates a $(LREF DateTime) from a string with the format
   3243         YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string.
   3244 
   3245         Params:
   3246             isoExtString = A string formatted in the ISO Extended format for dates
   3247                            and times.
   3248 
   3249         Throws:
   3250             $(REF DateTimeException,std,datetime,date) if the given string is
   3251             not in the ISO Extended format or if the resulting $(LREF DateTime)
   3252             would not be valid.
   3253       +/
   3254     static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
   3255         if (isSomeString!(S))
   3256     {
   3257         import std.algorithm.searching : countUntil;
   3258         import std.exception : enforce;
   3259         import std.format : format;
   3260         import std.string : strip;
   3261         import std.utf : byCodeUnit;
   3262 
   3263         auto str = strip(isoExtString);
   3264 
   3265         enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
   3266         auto t = str.byCodeUnit.countUntil('T');
   3267 
   3268         enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
   3269 
   3270         immutable date = Date.fromISOExtString(str[0 .. t]);
   3271         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
   3272 
   3273         return DateTime(date, tod);
   3274     }
   3275 
   3276     ///
   3277     @safe unittest
   3278     {
   3279         assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
   3280                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
   3281 
   3282         assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
   3283                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
   3284 
   3285         assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
   3286                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
   3287 
   3288         assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
   3289                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
   3290 
   3291         assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
   3292                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
   3293     }
   3294 
   3295     @safe unittest
   3296     {
   3297         assertThrown!DateTimeException(DateTime.fromISOExtString(""));
   3298         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
   3299         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
   3300         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
   3301         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
   3302         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
   3303 
   3304         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
   3305         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
   3306         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
   3307         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
   3308         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
   3309         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
   3310 
   3311         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
   3312         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
   3313         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
   3314         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
   3315 
   3316         assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
   3317         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
   3318 
   3319         assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
   3320         assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3321         assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   3322         assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3323         assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3324         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3325         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3326     }
   3327 
   3328     // https://issues.dlang.org/show_bug.cgi?id=17801
   3329     @safe unittest
   3330     {
   3331         import std.conv : to;
   3332         import std.meta : AliasSeq;
   3333         static foreach (C; AliasSeq!(char, wchar, dchar))
   3334         {
   3335             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   3336                 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
   3337         }
   3338     }
   3339 
   3340 
   3341     /++
   3342         Creates a $(LREF DateTime) from a string with the format
   3343         YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
   3344 
   3345         Params:
   3346             simpleString = A string formatted in the way that toSimpleString
   3347                            formats dates and times.
   3348 
   3349         Throws:
   3350             $(REF DateTimeException,std,datetime,date) if the given string is
   3351             not in the correct format or if the resulting $(LREF DateTime)
   3352             would not be valid.
   3353       +/
   3354     static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
   3355         if (isSomeString!(S))
   3356     {
   3357         import std.algorithm.searching : countUntil;
   3358         import std.exception : enforce;
   3359         import std.format : format;
   3360         import std.string : strip;
   3361         import std.utf : byCodeUnit;
   3362 
   3363         auto str = strip(simpleString);
   3364 
   3365         enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
   3366         auto t = str.byCodeUnit.countUntil(' ');
   3367 
   3368         enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
   3369 
   3370         immutable date = Date.fromSimpleString(str[0 .. t]);
   3371         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
   3372 
   3373         return DateTime(date, tod);
   3374     }
   3375 
   3376     ///
   3377     @safe unittest
   3378     {
   3379         assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
   3380                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
   3381         assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
   3382                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
   3383         assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
   3384                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
   3385         assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
   3386                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
   3387         assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
   3388                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
   3389     }
   3390 
   3391     @safe unittest
   3392     {
   3393         assertThrown!DateTimeException(DateTime.fromISOString(""));
   3394         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
   3395         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
   3396         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
   3397         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
   3398         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
   3399 
   3400         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
   3401         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
   3402         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
   3403         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
   3404         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
   3405 
   3406         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
   3407         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
   3408         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
   3409         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
   3410         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
   3411         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
   3412 
   3413         assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
   3414         assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
   3415 
   3416         assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
   3417                DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
   3418         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
   3419                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3420         assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
   3421                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
   3422         assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
   3423                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3424         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
   3425                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3426         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
   3427                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3428         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
   3429                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
   3430     }
   3431 
   3432     // https://issues.dlang.org/show_bug.cgi?id=17801
   3433     @safe unittest
   3434     {
   3435         import std.conv : to;
   3436         import std.meta : AliasSeq;
   3437         static foreach (C; AliasSeq!(char, wchar, dchar))
   3438         {
   3439             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   3440                 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
   3441         }
   3442     }
   3443 
   3444 
   3445     /++
   3446         Returns the $(LREF DateTime) farthest in the past which is representable
   3447         by $(LREF DateTime).
   3448       +/
   3449     @property static DateTime min() @safe pure nothrow @nogc
   3450     out(result)
   3451     {
   3452         assert(result._date == Date.min);
   3453         assert(result._tod == TimeOfDay.min);
   3454     }
   3455     do
   3456     {
   3457         auto dt = DateTime.init;
   3458         dt._date._year = short.min;
   3459         dt._date._month = Month.jan;
   3460         dt._date._day = 1;
   3461 
   3462         return dt;
   3463     }
   3464 
   3465     @safe unittest
   3466     {
   3467         assert(DateTime.min.year < 0);
   3468         assert(DateTime.min < DateTime.max);
   3469     }
   3470 
   3471 
   3472     /++
   3473         Returns the $(LREF DateTime) farthest in the future which is
   3474         representable by $(LREF DateTime).
   3475       +/
   3476     @property static DateTime max() @safe pure nothrow @nogc
   3477     out(result)
   3478     {
   3479         assert(result._date == Date.max);
   3480         assert(result._tod == TimeOfDay.max);
   3481     }
   3482     do
   3483     {
   3484         auto dt = DateTime.init;
   3485         dt._date._year = short.max;
   3486         dt._date._month = Month.dec;
   3487         dt._date._day = 31;
   3488         dt._tod._hour = TimeOfDay.maxHour;
   3489         dt._tod._minute = TimeOfDay.maxMinute;
   3490         dt._tod._second = TimeOfDay.maxSecond;
   3491 
   3492         return dt;
   3493     }
   3494 
   3495     @safe unittest
   3496     {
   3497         assert(DateTime.max.year > 0);
   3498         assert(DateTime.max > DateTime.min);
   3499     }
   3500 
   3501 
   3502 private:
   3503 
   3504     /+
   3505         Add seconds to the time of day. Negative values will subtract. If the
   3506         number of seconds overflows (or underflows), then the seconds will wrap,
   3507         increasing (or decreasing) the number of minutes accordingly. The
   3508         same goes for any larger units.
   3509 
   3510         Params:
   3511             seconds = The number of seconds to add to this $(LREF DateTime).
   3512       +/
   3513     ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
   3514     {
   3515         import core.time : convert;
   3516         long hnsecs = convert!("seconds", "hnsecs")(seconds);
   3517         hnsecs += convert!("hours", "hnsecs")(_tod._hour);
   3518         hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
   3519         hnsecs += convert!("seconds", "hnsecs")(_tod._second);
   3520 
   3521         auto days = splitUnitsFromHNSecs!"days"(hnsecs);
   3522 
   3523         if (hnsecs < 0)
   3524         {
   3525             hnsecs += convert!("days", "hnsecs")(1);
   3526             --days;
   3527         }
   3528 
   3529         _date._addDays(days);
   3530 
   3531         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
   3532         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
   3533         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
   3534 
   3535         _tod._hour = cast(ubyte) newHours;
   3536         _tod._minute = cast(ubyte) newMinutes;
   3537         _tod._second = cast(ubyte) newSeconds;
   3538 
   3539         return this;
   3540     }
   3541 
   3542     @safe unittest
   3543     {
   3544         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
   3545         {
   3546             orig._addSeconds(seconds);
   3547             assert(orig == expected);
   3548         }
   3549 
   3550         // Test A.D.
   3551         testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
   3552         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
   3553         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
   3554         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
   3555         testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
   3556         testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
   3557         testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
   3558         testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
   3559         testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
   3560         testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
   3561         testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
   3562         testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
   3563         testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
   3564         testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
   3565 
   3566         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
   3567         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
   3568         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
   3569         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
   3570         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
   3571         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
   3572         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
   3573         testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
   3574         testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
   3575 
   3576         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
   3577         testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
   3578         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
   3579         testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
   3580         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
   3581         testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
   3582         testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
   3583         testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
   3584         testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
   3585         testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
   3586         testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
   3587         testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
   3588         testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
   3589 
   3590         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
   3591         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
   3592         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
   3593         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
   3594         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
   3595         testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
   3596 
   3597         testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
   3598         testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
   3599         testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
   3600 
   3601         testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
   3602         testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
   3603         testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
   3604 
   3605         testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
   3606         testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
   3607         testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
   3608 
   3609         testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
   3610         testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
   3611         testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
   3612 
   3613         testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
   3614         testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
   3615         testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
   3616 
   3617         testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
   3618         testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
   3619         testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
   3620 
   3621         // Test B.C.
   3622         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
   3623         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
   3624         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
   3625         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
   3626         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
   3627         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
   3628         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
   3629         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
   3630         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
   3631         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
   3632         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
   3633         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
   3634         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
   3635         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
   3636 
   3637         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
   3638         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
   3639         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
   3640         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
   3641         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
   3642         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
   3643         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
   3644         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
   3645         testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
   3646 
   3647         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
   3648         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
   3649         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
   3650         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
   3651         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
   3652         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
   3653         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
   3654         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
   3655         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
   3656         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
   3657         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
   3658         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
   3659         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
   3660 
   3661         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
   3662         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
   3663         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
   3664         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
   3665         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
   3666         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
   3667         testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
   3668 
   3669         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
   3670         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
   3671         testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
   3672 
   3673         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
   3674         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
   3675         testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
   3676 
   3677         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
   3678         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
   3679         testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
   3680 
   3681         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
   3682         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
   3683         testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
   3684 
   3685         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
   3686         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
   3687         testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
   3688 
   3689         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
   3690         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
   3691         testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
   3692 
   3693         // Test Both
   3694         testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
   3695         testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
   3696 
   3697         testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
   3698         testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
   3699 
   3700         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
   3701         testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
   3702 
   3703         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
   3704         testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
   3705 
   3706         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
   3707         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
   3708         static assert(!__traits(compiles, cdt._addSeconds(4)));
   3709         static assert(!__traits(compiles, idt._addSeconds(4)));
   3710     }
   3711 
   3712 
   3713     Date      _date;
   3714     TimeOfDay _tod;
   3715 }
   3716 
   3717 ///
   3718 @safe pure unittest
   3719 {
   3720     import core.time : days, seconds;
   3721 
   3722     auto dt = DateTime(2000, 6, 1, 10, 30, 0);
   3723 
   3724     assert(dt.date == Date(2000, 6, 1));
   3725     assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
   3726     assert(dt.dayOfYear == 153);
   3727     assert(dt.dayOfWeek == DayOfWeek.thu);
   3728 
   3729     dt += 10.days + 100.seconds;
   3730     assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
   3731 
   3732     assert(dt.toISOExtString() == "2000-06-11T10:31:40");
   3733     assert(dt.toISOString() == "20000611T103140");
   3734     assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
   3735 
   3736     assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
   3737     assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
   3738     assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
   3739 }
   3740 
   3741 /++
   3742     Represents a date in the
   3743     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
   3744     Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
   3745     are A.D. Non-positive years are B.C.
   3746 
   3747     Year, month, and day are kept separately internally so that `Date` is
   3748     optimized for calendar-based operations.
   3749 
   3750     `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
   3751     leap year calculations for its entire length. As per
   3752     $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
   3753     year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
   3754     as a positive integer with 1 B.C. being the year prior to 1 A.D.
   3755 
   3756     Year 0 is a leap year.
   3757  +/
   3758 struct Date
   3759 {
   3760 public:
   3761 
   3762     /++
   3763         Throws:
   3764             $(REF DateTimeException,std,datetime,date) if the resulting
   3765             $(LREF Date) would not be valid.
   3766 
   3767         Params:
   3768             year  = Year of the Gregorian Calendar. Positive values are A.D.
   3769                     Non-positive values are B.C. with year 0 being the year
   3770                     prior to 1 A.D.
   3771             month = Month of the year (January is 1).
   3772             day   = Day of the month.
   3773      +/
   3774     this(int year, int month, int day) @safe pure
   3775     {
   3776         enforceValid!"months"(cast(Month) month);
   3777         enforceValid!"days"(year, cast(Month) month, day);
   3778 
   3779         _year  = cast(short) year;
   3780         _month = cast(Month) month;
   3781         _day   = cast(ubyte) day;
   3782     }
   3783 
   3784     @safe unittest
   3785     {
   3786         import std.exception : assertNotThrown;
   3787         assert(Date(1, 1, 1) == Date.init);
   3788 
   3789         static void testDate(Date date, int year, int month, int day)
   3790         {
   3791             assert(date._year == year);
   3792             assert(date._month == month);
   3793             assert(date._day == day);
   3794         }
   3795 
   3796         testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
   3797         testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
   3798         testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
   3799 
   3800         // Test A.D.
   3801         assertThrown!DateTimeException(Date(1, 0, 1));
   3802         assertThrown!DateTimeException(Date(1, 1, 0));
   3803         assertThrown!DateTimeException(Date(1999, 13, 1));
   3804         assertThrown!DateTimeException(Date(1999, 1, 32));
   3805         assertThrown!DateTimeException(Date(1999, 2, 29));
   3806         assertThrown!DateTimeException(Date(2000, 2, 30));
   3807         assertThrown!DateTimeException(Date(1999, 3, 32));
   3808         assertThrown!DateTimeException(Date(1999, 4, 31));
   3809         assertThrown!DateTimeException(Date(1999, 5, 32));
   3810         assertThrown!DateTimeException(Date(1999, 6, 31));
   3811         assertThrown!DateTimeException(Date(1999, 7, 32));
   3812         assertThrown!DateTimeException(Date(1999, 8, 32));
   3813         assertThrown!DateTimeException(Date(1999, 9, 31));
   3814         assertThrown!DateTimeException(Date(1999, 10, 32));
   3815         assertThrown!DateTimeException(Date(1999, 11, 31));
   3816         assertThrown!DateTimeException(Date(1999, 12, 32));
   3817 
   3818         assertNotThrown!DateTimeException(Date(1999, 1, 31));
   3819         assertNotThrown!DateTimeException(Date(1999, 2, 28));
   3820         assertNotThrown!DateTimeException(Date(2000, 2, 29));
   3821         assertNotThrown!DateTimeException(Date(1999, 3, 31));
   3822         assertNotThrown!DateTimeException(Date(1999, 4, 30));
   3823         assertNotThrown!DateTimeException(Date(1999, 5, 31));
   3824         assertNotThrown!DateTimeException(Date(1999, 6, 30));
   3825         assertNotThrown!DateTimeException(Date(1999, 7, 31));
   3826         assertNotThrown!DateTimeException(Date(1999, 8, 31));
   3827         assertNotThrown!DateTimeException(Date(1999, 9, 30));
   3828         assertNotThrown!DateTimeException(Date(1999, 10, 31));
   3829         assertNotThrown!DateTimeException(Date(1999, 11, 30));
   3830         assertNotThrown!DateTimeException(Date(1999, 12, 31));
   3831 
   3832         // Test B.C.
   3833         assertNotThrown!DateTimeException(Date(0, 1, 1));
   3834         assertNotThrown!DateTimeException(Date(-1, 1, 1));
   3835         assertNotThrown!DateTimeException(Date(-1, 12, 31));
   3836         assertNotThrown!DateTimeException(Date(-1, 2, 28));
   3837         assertNotThrown!DateTimeException(Date(-4, 2, 29));
   3838 
   3839         assertThrown!DateTimeException(Date(-1, 2, 29));
   3840         assertThrown!DateTimeException(Date(-2, 2, 29));
   3841         assertThrown!DateTimeException(Date(-3, 2, 29));
   3842     }
   3843 
   3844 
   3845     /++
   3846         Params:
   3847             day = The Xth day of the Gregorian Calendar that the constructed
   3848                   $(LREF Date) will be for.
   3849      +/
   3850     this(int day) @safe pure nothrow @nogc
   3851     {
   3852         if (day > 0)
   3853         {
   3854             int years = (day / daysIn400Years) * 400 + 1;
   3855             day %= daysIn400Years;
   3856 
   3857             {
   3858                 immutable tempYears = day / daysIn100Years;
   3859 
   3860                 if (tempYears == 4)
   3861                 {
   3862                     years += 300;
   3863                     day -= daysIn100Years * 3;
   3864                 }
   3865                 else
   3866                 {
   3867                     years += tempYears * 100;
   3868                     day %= daysIn100Years;
   3869                 }
   3870             }
   3871 
   3872             years += (day / daysIn4Years) * 4;
   3873             day %= daysIn4Years;
   3874 
   3875             {
   3876                 immutable tempYears = day / daysInYear;
   3877 
   3878                 if (tempYears == 4)
   3879                 {
   3880                     years += 3;
   3881                     day -= daysInYear * 3;
   3882                 }
   3883                 else
   3884                 {
   3885                     years += tempYears;
   3886                     day %= daysInYear;
   3887                 }
   3888             }
   3889 
   3890             if (day == 0)
   3891             {
   3892                 _year = cast(short)(years - 1);
   3893                 _month = Month.dec;
   3894                 _day = 31;
   3895             }
   3896             else
   3897             {
   3898                 _year = cast(short) years;
   3899 
   3900                 setDayOfYear(day);
   3901             }
   3902         }
   3903         else if (day <= 0 && -day < daysInLeapYear)
   3904         {
   3905             _year = 0;
   3906 
   3907             setDayOfYear(daysInLeapYear + day);
   3908         }
   3909         else
   3910         {
   3911             day += daysInLeapYear - 1;
   3912             int years = (day / daysIn400Years) * 400 - 1;
   3913             day %= daysIn400Years;
   3914 
   3915             {
   3916                 immutable tempYears = day / daysIn100Years;
   3917 
   3918                 if (tempYears == -4)
   3919                 {
   3920                     years -= 300;
   3921                     day += daysIn100Years * 3;
   3922                 }
   3923                 else
   3924                 {
   3925                     years += tempYears * 100;
   3926                     day %= daysIn100Years;
   3927                 }
   3928             }
   3929 
   3930             years += (day / daysIn4Years) * 4;
   3931             day %= daysIn4Years;
   3932 
   3933             {
   3934                 immutable tempYears = day / daysInYear;
   3935 
   3936                 if (tempYears == -4)
   3937                 {
   3938                     years -= 3;
   3939                     day += daysInYear * 3;
   3940                 }
   3941                 else
   3942                 {
   3943                     years += tempYears;
   3944                     day %= daysInYear;
   3945                 }
   3946             }
   3947 
   3948             if (day == 0)
   3949             {
   3950                 _year = cast(short)(years + 1);
   3951                 _month = Month.jan;
   3952                 _day = 1;
   3953             }
   3954             else
   3955             {
   3956                 _year = cast(short) years;
   3957                 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
   3958 
   3959                 setDayOfYear(newDoY);
   3960             }
   3961         }
   3962     }
   3963 
   3964     @safe unittest
   3965     {
   3966         import std.range : chain;
   3967 
   3968         // Test A.D.
   3969         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
   3970             assert(Date(gd.day) == gd.date);
   3971     }
   3972 
   3973 
   3974     /++
   3975         Compares this $(LREF Date) with the given $(LREF Date).
   3976 
   3977         Returns:
   3978             $(BOOKTABLE,
   3979             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
   3980             $(TR $(TD this == rhs) $(TD 0))
   3981             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
   3982             )
   3983      +/
   3984     int opCmp(Date rhs) const @safe pure nothrow @nogc
   3985     {
   3986         if (_year < rhs._year)
   3987             return -1;
   3988         if (_year > rhs._year)
   3989             return 1;
   3990 
   3991         if (_month < rhs._month)
   3992             return -1;
   3993         if (_month > rhs._month)
   3994             return 1;
   3995 
   3996         if (_day < rhs._day)
   3997             return -1;
   3998         if (_day > rhs._day)
   3999             return 1;
   4000 
   4001         return 0;
   4002     }
   4003 
   4004     @safe unittest
   4005     {
   4006         // Test A.D.
   4007         assert(Date(1, 1, 1).opCmp(Date.init) == 0);
   4008 
   4009         assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
   4010         assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
   4011         assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
   4012 
   4013         assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
   4014         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
   4015 
   4016         assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
   4017 
   4018         assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
   4019         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
   4020         assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
   4021         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
   4022         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
   4023         assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
   4024 
   4025         assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
   4026         assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
   4027         assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
   4028         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
   4029         assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
   4030         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
   4031 
   4032         // Test B.C.
   4033         assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
   4034         assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
   4035         assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
   4036         assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
   4037 
   4038         assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
   4039         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
   4040 
   4041         assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
   4042 
   4043         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
   4044         assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
   4045         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
   4046         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
   4047         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
   4048         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
   4049 
   4050         assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
   4051         assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
   4052         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
   4053         assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
   4054         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
   4055         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
   4056 
   4057         // Test Both
   4058         assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
   4059         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
   4060 
   4061         assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
   4062         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
   4063 
   4064         assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
   4065         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
   4066 
   4067         assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
   4068         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
   4069 
   4070         assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
   4071         assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
   4072 
   4073         auto date = Date(1999, 7, 6);
   4074         const cdate = Date(1999, 7, 6);
   4075         immutable idate = Date(1999, 7, 6);
   4076         assert(date.opCmp(date) == 0);
   4077         assert(date.opCmp(cdate) == 0);
   4078         assert(date.opCmp(idate) == 0);
   4079         assert(cdate.opCmp(date) == 0);
   4080         assert(cdate.opCmp(cdate) == 0);
   4081         assert(cdate.opCmp(idate) == 0);
   4082         assert(idate.opCmp(date) == 0);
   4083         assert(idate.opCmp(cdate) == 0);
   4084         assert(idate.opCmp(idate) == 0);
   4085     }
   4086 
   4087 
   4088     /++
   4089         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
   4090         are B.C.
   4091      +/
   4092     @property short year() const @safe pure nothrow @nogc
   4093     {
   4094         return _year;
   4095     }
   4096 
   4097     ///
   4098     @safe unittest
   4099     {
   4100         assert(Date(1999, 7, 6).year == 1999);
   4101         assert(Date(2010, 10, 4).year == 2010);
   4102         assert(Date(-7, 4, 5).year == -7);
   4103     }
   4104 
   4105     @safe unittest
   4106     {
   4107         assert(Date.init.year == 1);
   4108         assert(Date(1999, 7, 6).year == 1999);
   4109         assert(Date(-1999, 7, 6).year == -1999);
   4110 
   4111         const cdate = Date(1999, 7, 6);
   4112         immutable idate = Date(1999, 7, 6);
   4113         assert(cdate.year == 1999);
   4114         assert(idate.year == 1999);
   4115     }
   4116 
   4117     /++
   4118         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
   4119         are B.C.
   4120 
   4121         Params:
   4122             year = The year to set this Date's year to.
   4123 
   4124         Throws:
   4125             $(REF DateTimeException,std,datetime,date) if the new year is not
   4126             a leap year and the resulting date would be on February 29th.
   4127      +/
   4128     @property void year(int year) @safe pure
   4129     {
   4130         enforceValid!"days"(year, _month, _day);
   4131         _year = cast(short) year;
   4132     }
   4133 
   4134     ///
   4135     @safe unittest
   4136     {
   4137         assert(Date(1999, 7, 6).year == 1999);
   4138         assert(Date(2010, 10, 4).year == 2010);
   4139         assert(Date(-7, 4, 5).year == -7);
   4140     }
   4141 
   4142     @safe unittest
   4143     {
   4144         static void testDateInvalid(Date date, int year)
   4145         {
   4146             date.year = year;
   4147         }
   4148 
   4149         static void testDate(Date date, int year, Date expected)
   4150         {
   4151             date.year = year;
   4152             assert(date == expected);
   4153         }
   4154 
   4155         assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
   4156 
   4157         testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
   4158         testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
   4159         testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
   4160 
   4161         const cdate = Date(1999, 7, 6);
   4162         immutable idate = Date(1999, 7, 6);
   4163         static assert(!__traits(compiles, cdate.year = 1999));
   4164         static assert(!__traits(compiles, idate.year = 1999));
   4165     }
   4166 
   4167 
   4168     /++
   4169         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
   4170 
   4171         Throws:
   4172             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
   4173      +/
   4174     @property ushort yearBC() const @safe pure
   4175     {
   4176         import std.format : format;
   4177 
   4178         if (isAD)
   4179             throw new DateTimeException(format("Year %s is A.D.", _year));
   4180         return cast(ushort)((_year * -1) + 1);
   4181     }
   4182 
   4183     ///
   4184     @safe unittest
   4185     {
   4186         assert(Date(0, 1, 1).yearBC == 1);
   4187         assert(Date(-1, 1, 1).yearBC == 2);
   4188         assert(Date(-100, 1, 1).yearBC == 101);
   4189     }
   4190 
   4191     @safe unittest
   4192     {
   4193         assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
   4194 
   4195         auto date = Date(0, 7, 6);
   4196         const cdate = Date(0, 7, 6);
   4197         immutable idate = Date(0, 7, 6);
   4198         assert(date.yearBC == 1);
   4199         assert(cdate.yearBC == 1);
   4200         assert(idate.yearBC == 1);
   4201     }
   4202 
   4203 
   4204     /++
   4205         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
   4206 
   4207         Params:
   4208             year = The year B.C. to set this $(LREF Date)'s year to.
   4209 
   4210         Throws:
   4211             $(REF DateTimeException,std,datetime,date) if a non-positive value
   4212             is given.
   4213      +/
   4214     @property void yearBC(int year) @safe pure
   4215     {
   4216         if (year <= 0)
   4217             throw new DateTimeException("The given year is not a year B.C.");
   4218         _year = cast(short)((year - 1) * -1);
   4219     }
   4220 
   4221     ///
   4222     @safe unittest
   4223     {
   4224         auto date = Date(2010, 1, 1);
   4225         date.yearBC = 1;
   4226         assert(date == Date(0, 1, 1));
   4227 
   4228         date.yearBC = 10;
   4229         assert(date == Date(-9, 1, 1));
   4230     }
   4231 
   4232     @safe unittest
   4233     {
   4234         assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
   4235 
   4236         auto date = Date(0, 7, 6);
   4237         const cdate = Date(0, 7, 6);
   4238         immutable idate = Date(0, 7, 6);
   4239         date.yearBC = 7;
   4240         assert(date.yearBC == 7);
   4241         static assert(!__traits(compiles, cdate.yearBC = 7));
   4242         static assert(!__traits(compiles, idate.yearBC = 7));
   4243     }
   4244 
   4245 
   4246     /++
   4247         Month of a Gregorian Year.
   4248      +/
   4249     @property Month month() const @safe pure nothrow @nogc
   4250     {
   4251         return _month;
   4252     }
   4253 
   4254     ///
   4255     @safe unittest
   4256     {
   4257         assert(Date(1999, 7, 6).month == 7);
   4258         assert(Date(2010, 10, 4).month == 10);
   4259         assert(Date(-7, 4, 5).month == 4);
   4260     }
   4261 
   4262     @safe unittest
   4263     {
   4264         assert(Date.init.month == 1);
   4265         assert(Date(1999, 7, 6).month == 7);
   4266         assert(Date(-1999, 7, 6).month == 7);
   4267 
   4268         const cdate = Date(1999, 7, 6);
   4269         immutable idate = Date(1999, 7, 6);
   4270         assert(cdate.month == 7);
   4271         assert(idate.month == 7);
   4272     }
   4273 
   4274     /++
   4275         Month of a Gregorian Year.
   4276 
   4277         Params:
   4278             month = The month to set this $(LREF Date)'s month to.
   4279 
   4280         Throws:
   4281             $(REF DateTimeException,std,datetime,date) if the given month is
   4282             not a valid month or if the current day would not be valid in the
   4283             given month.
   4284      +/
   4285     @property void month(Month month) @safe pure
   4286     {
   4287         enforceValid!"months"(month);
   4288         enforceValid!"days"(_year, month, _day);
   4289         _month = cast(Month) month;
   4290     }
   4291 
   4292     @safe unittest
   4293     {
   4294         static void testDate(Date date, Month month, Date expected = Date.init)
   4295         {
   4296             date.month = month;
   4297             assert(expected != Date.init);
   4298             assert(date == expected);
   4299         }
   4300 
   4301         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
   4302         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
   4303         assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
   4304         assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
   4305 
   4306         testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
   4307         testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
   4308 
   4309         const cdate = Date(1999, 7, 6);
   4310         immutable idate = Date(1999, 7, 6);
   4311         static assert(!__traits(compiles, cdate.month = 7));
   4312         static assert(!__traits(compiles, idate.month = 7));
   4313     }
   4314 
   4315 
   4316     /++
   4317         Day of a Gregorian Month.
   4318      +/
   4319     @property ubyte day() const @safe pure nothrow @nogc
   4320     {
   4321         return _day;
   4322     }
   4323 
   4324     ///
   4325     @safe unittest
   4326     {
   4327         assert(Date(1999, 7, 6).day == 6);
   4328         assert(Date(2010, 10, 4).day == 4);
   4329         assert(Date(-7, 4, 5).day == 5);
   4330     }
   4331 
   4332     @safe unittest
   4333     {
   4334         import std.format : format;
   4335         import std.range : chain;
   4336 
   4337         static void test(Date date, int expected)
   4338         {
   4339             assert(date.day == expected, format("Value given: %s", date));
   4340         }
   4341 
   4342         foreach (year; chain(testYearsBC, testYearsAD))
   4343         {
   4344             foreach (md; testMonthDays)
   4345                 test(Date(year, md.month, md.day), md.day);
   4346         }
   4347 
   4348         const cdate = Date(1999, 7, 6);
   4349         immutable idate = Date(1999, 7, 6);
   4350         assert(cdate.day == 6);
   4351         assert(idate.day == 6);
   4352     }
   4353 
   4354     /++
   4355         Day of a Gregorian Month.
   4356 
   4357         Params:
   4358             day = The day of the month to set this $(LREF Date)'s day to.
   4359 
   4360         Throws:
   4361             $(REF DateTimeException,std,datetime,date) if the given day is not
   4362             a valid day of the current month.
   4363      +/
   4364     @property void day(int day) @safe pure
   4365     {
   4366         enforceValid!"days"(_year, _month, day);
   4367         _day = cast(ubyte) day;
   4368     }
   4369 
   4370     @safe unittest
   4371     {
   4372         import std.exception : assertNotThrown;
   4373 
   4374         static void testDate(Date date, int day)
   4375         {
   4376             date.day = day;
   4377         }
   4378 
   4379         // Test A.D.
   4380         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
   4381         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
   4382         assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
   4383         assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
   4384         assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
   4385         assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
   4386         assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
   4387         assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
   4388         assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
   4389         assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
   4390         assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
   4391         assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
   4392         assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
   4393         assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
   4394 
   4395         assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
   4396         assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
   4397         assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
   4398         assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
   4399         assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
   4400         assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
   4401         assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
   4402         assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
   4403         assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
   4404         assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
   4405         assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
   4406         assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
   4407         assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
   4408 
   4409         {
   4410             auto date = Date(1, 1, 1);
   4411             date.day = 6;
   4412             assert(date == Date(1, 1, 6));
   4413         }
   4414 
   4415         // Test B.C.
   4416         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
   4417         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
   4418         assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
   4419         assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
   4420         assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
   4421         assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
   4422         assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
   4423         assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
   4424         assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
   4425         assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
   4426         assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
   4427         assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
   4428         assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
   4429         assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
   4430 
   4431         assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
   4432         assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
   4433         assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
   4434         assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
   4435         assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
   4436         assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
   4437         assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
   4438         assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
   4439         assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
   4440         assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
   4441         assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
   4442         assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
   4443         assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
   4444 
   4445         {
   4446             auto date = Date(-1, 1, 1);
   4447             date.day = 6;
   4448             assert(date == Date(-1, 1, 6));
   4449         }
   4450 
   4451         const cdate = Date(1999, 7, 6);
   4452         immutable idate = Date(1999, 7, 6);
   4453         static assert(!__traits(compiles, cdate.day = 6));
   4454         static assert(!__traits(compiles, idate.day = 6));
   4455     }
   4456 
   4457 
   4458     /++
   4459         Adds the given number of years or months to this $(LREF Date), mutating
   4460         it. A negative number will subtract.
   4461 
   4462         Note that if day overflow is allowed, and the date with the adjusted
   4463         year/month overflows the number of days in the new month, then the month
   4464         will be incremented by one, and the day set to the number of days
   4465         overflowed. (e.g. if the day were 31 and the new month were June, then
   4466         the month would be incremented to July, and the new day would be 1). If
   4467         day overflow is not allowed, then the day will be set to the last valid
   4468         day in the month (e.g. June 31st would become June 30th).
   4469 
   4470         Params:
   4471             units         = The type of units to add ("years" or "months").
   4472             value         = The number of months or years to add to this
   4473                             $(LREF Date).
   4474             allowOverflow = Whether the day should be allowed to overflow,
   4475                             causing the month to increment.
   4476 
   4477         Returns:
   4478             A reference to the `Date` (`this`).
   4479       +/
   4480     @safe pure nothrow @nogc
   4481     ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
   4482         if (units == "years")
   4483     {
   4484         _year += value;
   4485 
   4486         if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
   4487         {
   4488             if (allowOverflow == AllowDayOverflow.yes)
   4489             {
   4490                 _month = Month.mar;
   4491                 _day = 1;
   4492             }
   4493             else
   4494                 _day = 28;
   4495         }
   4496 
   4497         return this;
   4498     }
   4499 
   4500     ///
   4501     @safe unittest
   4502     {
   4503         auto d1 = Date(2010, 1, 1);
   4504         d1.add!"months"(11);
   4505         assert(d1 == Date(2010, 12, 1));
   4506 
   4507         auto d2 = Date(2010, 1, 1);
   4508         d2.add!"months"(-11);
   4509         assert(d2 == Date(2009, 2, 1));
   4510 
   4511         auto d3 = Date(2000, 2, 29);
   4512         d3.add!"years"(1);
   4513         assert(d3 == Date(2001, 3, 1));
   4514 
   4515         auto d4 = Date(2000, 2, 29);
   4516         d4.add!"years"(1, AllowDayOverflow.no);
   4517         assert(d4 == Date(2001, 2, 28));
   4518     }
   4519 
   4520     // Test add!"years"() with AllowDayOverflow.yes
   4521     @safe unittest
   4522     {
   4523         // Test A.D.
   4524         {
   4525             auto date = Date(1999, 7, 6);
   4526             date.add!"years"(7);
   4527             assert(date == Date(2006, 7, 6));
   4528             date.add!"years"(-9);
   4529             assert(date == Date(1997, 7, 6));
   4530         }
   4531 
   4532         {
   4533             auto date = Date(1999, 2, 28);
   4534             date.add!"years"(1);
   4535             assert(date == Date(2000, 2, 28));
   4536         }
   4537 
   4538         {
   4539             auto date = Date(2000, 2, 29);
   4540             date.add!"years"(-1);
   4541             assert(date == Date(1999, 3, 1));
   4542         }
   4543 
   4544         // Test B.C.
   4545         {
   4546             auto date = Date(-1999, 7, 6);
   4547             date.add!"years"(-7);
   4548             assert(date == Date(-2006, 7, 6));
   4549             date.add!"years"(9);
   4550             assert(date == Date(-1997, 7, 6));
   4551         }
   4552 
   4553         {
   4554             auto date = Date(-1999, 2, 28);
   4555             date.add!"years"(-1);
   4556             assert(date == Date(-2000, 2, 28));
   4557         }
   4558 
   4559         {
   4560             auto date = Date(-2000, 2, 29);
   4561             date.add!"years"(1);
   4562             assert(date == Date(-1999, 3, 1));
   4563         }
   4564 
   4565         // Test Both
   4566         {
   4567             auto date = Date(4, 7, 6);
   4568             date.add!"years"(-5);
   4569             assert(date == Date(-1, 7, 6));
   4570             date.add!"years"(5);
   4571             assert(date == Date(4, 7, 6));
   4572         }
   4573 
   4574         {
   4575             auto date = Date(-4, 7, 6);
   4576             date.add!"years"(5);
   4577             assert(date == Date(1, 7, 6));
   4578             date.add!"years"(-5);
   4579             assert(date == Date(-4, 7, 6));
   4580         }
   4581 
   4582         {
   4583             auto date = Date(4, 7, 6);
   4584             date.add!"years"(-8);
   4585             assert(date == Date(-4, 7, 6));
   4586             date.add!"years"(8);
   4587             assert(date == Date(4, 7, 6));
   4588         }
   4589 
   4590         {
   4591             auto date = Date(-4, 7, 6);
   4592             date.add!"years"(8);
   4593             assert(date == Date(4, 7, 6));
   4594             date.add!"years"(-8);
   4595             assert(date == Date(-4, 7, 6));
   4596         }
   4597 
   4598         {
   4599             auto date = Date(-4, 2, 29);
   4600             date.add!"years"(5);
   4601             assert(date == Date(1, 3, 1));
   4602         }
   4603 
   4604         {
   4605             auto date = Date(4, 2, 29);
   4606             date.add!"years"(-5);
   4607             assert(date == Date(-1, 3, 1));
   4608         }
   4609 
   4610         {
   4611             auto date = Date(4, 2, 29);
   4612             date.add!"years"(-5).add!"years"(7);
   4613             assert(date == Date(6, 3, 1));
   4614         }
   4615 
   4616         const cdate = Date(1999, 7, 6);
   4617         immutable idate = Date(1999, 7, 6);
   4618         static assert(!__traits(compiles, cdate.add!"years"(7)));
   4619         static assert(!__traits(compiles, idate.add!"years"(7)));
   4620     }
   4621 
   4622     // Test add!"years"() with AllowDayOverflow.no
   4623     @safe unittest
   4624     {
   4625         // Test A.D.
   4626         {
   4627             auto date = Date(1999, 7, 6);
   4628             date.add!"years"(7, AllowDayOverflow.no);
   4629             assert(date == Date(2006, 7, 6));
   4630             date.add!"years"(-9, AllowDayOverflow.no);
   4631             assert(date == Date(1997, 7, 6));
   4632         }
   4633 
   4634         {
   4635             auto date = Date(1999, 2, 28);
   4636             date.add!"years"(1, AllowDayOverflow.no);
   4637             assert(date == Date(2000, 2, 28));
   4638         }
   4639 
   4640         {
   4641             auto date = Date(2000, 2, 29);
   4642             date.add!"years"(-1, AllowDayOverflow.no);
   4643             assert(date == Date(1999, 2, 28));
   4644         }
   4645 
   4646         // Test B.C.
   4647         {
   4648             auto date = Date(-1999, 7, 6);
   4649             date.add!"years"(-7, AllowDayOverflow.no);
   4650             assert(date == Date(-2006, 7, 6));
   4651             date.add!"years"(9, AllowDayOverflow.no);
   4652             assert(date == Date(-1997, 7, 6));
   4653         }
   4654 
   4655         {
   4656             auto date = Date(-1999, 2, 28);
   4657             date.add!"years"(-1, AllowDayOverflow.no);
   4658             assert(date == Date(-2000, 2, 28));
   4659         }
   4660 
   4661         {
   4662             auto date = Date(-2000, 2, 29);
   4663             date.add!"years"(1, AllowDayOverflow.no);
   4664             assert(date == Date(-1999, 2, 28));
   4665         }
   4666 
   4667         // Test Both
   4668         {
   4669             auto date = Date(4, 7, 6);
   4670             date.add!"years"(-5, AllowDayOverflow.no);
   4671             assert(date == Date(-1, 7, 6));
   4672             date.add!"years"(5, AllowDayOverflow.no);
   4673             assert(date == Date(4, 7, 6));
   4674         }
   4675 
   4676         {
   4677             auto date = Date(-4, 7, 6);
   4678             date.add!"years"(5, AllowDayOverflow.no);
   4679             assert(date == Date(1, 7, 6));
   4680             date.add!"years"(-5, AllowDayOverflow.no);
   4681             assert(date == Date(-4, 7, 6));
   4682         }
   4683 
   4684         {
   4685             auto date = Date(4, 7, 6);
   4686             date.add!"years"(-8, AllowDayOverflow.no);
   4687             assert(date == Date(-4, 7, 6));
   4688             date.add!"years"(8, AllowDayOverflow.no);
   4689             assert(date == Date(4, 7, 6));
   4690         }
   4691 
   4692         {
   4693             auto date = Date(-4, 7, 6);
   4694             date.add!"years"(8, AllowDayOverflow.no);
   4695             assert(date == Date(4, 7, 6));
   4696             date.add!"years"(-8, AllowDayOverflow.no);
   4697             assert(date == Date(-4, 7, 6));
   4698         }
   4699 
   4700         {
   4701             auto date = Date(-4, 2, 29);
   4702             date.add!"years"(5, AllowDayOverflow.no);
   4703             assert(date == Date(1, 2, 28));
   4704         }
   4705 
   4706         {
   4707             auto date = Date(4, 2, 29);
   4708             date.add!"years"(-5, AllowDayOverflow.no);
   4709             assert(date == Date(-1, 2, 28));
   4710         }
   4711 
   4712         {
   4713             auto date = Date(4, 2, 29);
   4714             date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
   4715             assert(date == Date(6, 2, 28));
   4716         }
   4717     }
   4718 
   4719 
   4720     // Shares documentation with "years" version.
   4721     @safe pure nothrow @nogc
   4722     ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
   4723         if (units == "months")
   4724     {
   4725         auto years = months / 12;
   4726         months %= 12;
   4727         auto newMonth = _month + months;
   4728 
   4729         if (months < 0)
   4730         {
   4731             if (newMonth < 1)
   4732             {
   4733                 newMonth += 12;
   4734                 --years;
   4735             }
   4736         }
   4737         else if (newMonth > 12)
   4738         {
   4739             newMonth -= 12;
   4740             ++years;
   4741         }
   4742 
   4743         _year += years;
   4744         _month = cast(Month) newMonth;
   4745 
   4746         immutable currMaxDay = maxDay(_year, _month);
   4747         immutable overflow = _day - currMaxDay;
   4748 
   4749         if (overflow > 0)
   4750         {
   4751             if (allowOverflow == AllowDayOverflow.yes)
   4752             {
   4753                 ++_month;
   4754                 _day = cast(ubyte) overflow;
   4755             }
   4756             else
   4757                 _day = cast(ubyte) currMaxDay;
   4758         }
   4759 
   4760         return this;
   4761     }
   4762 
   4763     // Test add!"months"() with AllowDayOverflow.yes
   4764     @safe unittest
   4765     {
   4766         // Test A.D.
   4767         {
   4768             auto date = Date(1999, 7, 6);
   4769             date.add!"months"(3);
   4770             assert(date == Date(1999, 10, 6));
   4771             date.add!"months"(-4);
   4772             assert(date == Date(1999, 6, 6));
   4773         }
   4774 
   4775         {
   4776             auto date = Date(1999, 7, 6);
   4777             date.add!"months"(6);
   4778             assert(date == Date(2000, 1, 6));
   4779             date.add!"months"(-6);
   4780             assert(date == Date(1999, 7, 6));
   4781         }
   4782 
   4783         {
   4784             auto date = Date(1999, 7, 6);
   4785             date.add!"months"(27);
   4786             assert(date == Date(2001, 10, 6));
   4787             date.add!"months"(-28);
   4788             assert(date == Date(1999, 6, 6));
   4789         }
   4790 
   4791         {
   4792             auto date = Date(1999, 5, 31);
   4793             date.add!"months"(1);
   4794             assert(date == Date(1999, 7, 1));
   4795         }
   4796 
   4797         {
   4798             auto date = Date(1999, 5, 31);
   4799             date.add!"months"(-1);
   4800             assert(date == Date(1999, 5, 1));
   4801         }
   4802 
   4803         {
   4804             auto date = Date(1999, 2, 28);
   4805             date.add!"months"(12);
   4806             assert(date == Date(2000, 2, 28));
   4807         }
   4808 
   4809         {
   4810             auto date = Date(2000, 2, 29);
   4811             date.add!"months"(12);
   4812             assert(date == Date(2001, 3, 1));
   4813         }
   4814 
   4815         {
   4816             auto date = Date(1999, 7, 31);
   4817             date.add!"months"(1);
   4818             assert(date == Date(1999, 8, 31));
   4819             date.add!"months"(1);
   4820             assert(date == Date(1999, 10, 1));
   4821         }
   4822 
   4823         {
   4824             auto date = Date(1998, 8, 31);
   4825             date.add!"months"(13);
   4826             assert(date == Date(1999, 10, 1));
   4827             date.add!"months"(-13);
   4828             assert(date == Date(1998, 9, 1));
   4829         }
   4830 
   4831         {
   4832             auto date = Date(1997, 12, 31);
   4833             date.add!"months"(13);
   4834             assert(date == Date(1999, 1, 31));
   4835             date.add!"months"(-13);
   4836             assert(date == Date(1997, 12, 31));
   4837         }
   4838 
   4839         {
   4840             auto date = Date(1997, 12, 31);
   4841             date.add!"months"(14);
   4842             assert(date == Date(1999, 3, 3));
   4843             date.add!"months"(-14);
   4844             assert(date == Date(1998, 1, 3));
   4845         }
   4846 
   4847         {
   4848             auto date = Date(1998, 12, 31);
   4849             date.add!"months"(14);
   4850             assert(date == Date(2000, 3, 2));
   4851             date.add!"months"(-14);
   4852             assert(date == Date(1999, 1, 2));
   4853         }
   4854 
   4855         {
   4856             auto date = Date(1999, 12, 31);
   4857             date.add!"months"(14);
   4858             assert(date == Date(2001, 3, 3));
   4859             date.add!"months"(-14);
   4860             assert(date == Date(2000, 1, 3));
   4861         }
   4862 
   4863         // Test B.C.
   4864         {
   4865             auto date = Date(-1999, 7, 6);
   4866             date.add!"months"(3);
   4867             assert(date == Date(-1999, 10, 6));
   4868             date.add!"months"(-4);
   4869             assert(date == Date(-1999, 6, 6));
   4870         }
   4871 
   4872         {
   4873             auto date = Date(-1999, 7, 6);
   4874             date.add!"months"(6);
   4875             assert(date == Date(-1998, 1, 6));
   4876             date.add!"months"(-6);
   4877             assert(date == Date(-1999, 7, 6));
   4878         }
   4879 
   4880         {
   4881             auto date = Date(-1999, 7, 6);
   4882             date.add!"months"(-27);
   4883             assert(date == Date(-2001, 4, 6));
   4884             date.add!"months"(28);
   4885             assert(date == Date(-1999, 8, 6));
   4886         }
   4887 
   4888         {
   4889             auto date = Date(-1999, 5, 31);
   4890             date.add!"months"(1);
   4891             assert(date == Date(-1999, 7, 1));
   4892         }
   4893 
   4894         {
   4895             auto date = Date(-1999, 5, 31);
   4896             date.add!"months"(-1);
   4897             assert(date == Date(-1999, 5, 1));
   4898         }
   4899 
   4900         {
   4901             auto date = Date(-1999, 2, 28);
   4902             date.add!"months"(-12);
   4903             assert(date == Date(-2000, 2, 28));
   4904         }
   4905 
   4906         {
   4907             auto date = Date(-2000, 2, 29);
   4908             date.add!"months"(-12);
   4909             assert(date == Date(-2001, 3, 1));
   4910         }
   4911 
   4912         {
   4913             auto date = Date(-1999, 7, 31);
   4914             date.add!"months"(1);
   4915             assert(date == Date(-1999, 8, 31));
   4916             date.add!"months"(1);
   4917             assert(date == Date(-1999, 10, 1));
   4918         }
   4919 
   4920         {
   4921             auto date = Date(-1998, 8, 31);
   4922             date.add!"months"(13);
   4923             assert(date == Date(-1997, 10, 1));
   4924             date.add!"months"(-13);
   4925             assert(date == Date(-1998, 9, 1));
   4926         }
   4927 
   4928         {
   4929             auto date = Date(-1997, 12, 31);
   4930             date.add!"months"(13);
   4931             assert(date == Date(-1995, 1, 31));
   4932             date.add!"months"(-13);
   4933             assert(date == Date(-1997, 12, 31));
   4934         }
   4935 
   4936         {
   4937             auto date = Date(-1997, 12, 31);
   4938             date.add!"months"(14);
   4939             assert(date == Date(-1995, 3, 3));
   4940             date.add!"months"(-14);
   4941             assert(date == Date(-1996, 1, 3));
   4942         }
   4943 
   4944         {
   4945             auto date = Date(-2002, 12, 31);
   4946             date.add!"months"(14);
   4947             assert(date == Date(-2000, 3, 2));
   4948             date.add!"months"(-14);
   4949             assert(date == Date(-2001, 1, 2));
   4950         }
   4951 
   4952         {
   4953             auto date = Date(-2001, 12, 31);
   4954             date.add!"months"(14);
   4955             assert(date == Date(-1999, 3, 3));
   4956             date.add!"months"(-14);
   4957             assert(date == Date(-2000, 1, 3));
   4958         }
   4959 
   4960         // Test Both
   4961         {
   4962             auto date = Date(1, 1, 1);
   4963             date.add!"months"(-1);
   4964             assert(date == Date(0, 12, 1));
   4965             date.add!"months"(1);
   4966             assert(date == Date(1, 1, 1));
   4967         }
   4968 
   4969         {
   4970             auto date = Date(4, 1, 1);
   4971             date.add!"months"(-48);
   4972             assert(date == Date(0, 1, 1));
   4973             date.add!"months"(48);
   4974             assert(date == Date(4, 1, 1));
   4975         }
   4976 
   4977         {
   4978             auto date = Date(4, 3, 31);
   4979             date.add!"months"(-49);
   4980             assert(date == Date(0, 3, 2));
   4981             date.add!"months"(49);
   4982             assert(date == Date(4, 4, 2));
   4983         }
   4984 
   4985         {
   4986             auto date = Date(4, 3, 31);
   4987             date.add!"months"(-85);
   4988             assert(date == Date(-3, 3, 3));
   4989             date.add!"months"(85);
   4990             assert(date == Date(4, 4, 3));
   4991         }
   4992 
   4993         {
   4994             auto date = Date(-3, 3, 31);
   4995             date.add!"months"(85).add!"months"(-83);
   4996             assert(date == Date(-3, 6, 1));
   4997         }
   4998 
   4999         const cdate = Date(1999, 7, 6);
   5000         immutable idate = Date(1999, 7, 6);
   5001         static assert(!__traits(compiles, cdate.add!"months"(3)));
   5002         static assert(!__traits(compiles, idate.add!"months"(3)));
   5003     }
   5004 
   5005     // Test add!"months"() with AllowDayOverflow.no
   5006     @safe unittest
   5007     {
   5008         // Test A.D.
   5009         {
   5010             auto date = Date(1999, 7, 6);
   5011             date.add!"months"(3, AllowDayOverflow.no);
   5012             assert(date == Date(1999, 10, 6));
   5013             date.add!"months"(-4, AllowDayOverflow.no);
   5014             assert(date == Date(1999, 6, 6));
   5015         }
   5016 
   5017         {
   5018             auto date = Date(1999, 7, 6);
   5019             date.add!"months"(6, AllowDayOverflow.no);
   5020             assert(date == Date(2000, 1, 6));
   5021             date.add!"months"(-6, AllowDayOverflow.no);
   5022             assert(date == Date(1999, 7, 6));
   5023         }
   5024 
   5025         {
   5026             auto date = Date(1999, 7, 6);
   5027             date.add!"months"(27, AllowDayOverflow.no);
   5028             assert(date == Date(2001, 10, 6));
   5029             date.add!"months"(-28, AllowDayOverflow.no);
   5030             assert(date == Date(1999, 6, 6));
   5031         }
   5032 
   5033         {
   5034             auto date = Date(1999, 5, 31);
   5035             date.add!"months"(1, AllowDayOverflow.no);
   5036             assert(date == Date(1999, 6, 30));
   5037         }
   5038 
   5039         {
   5040             auto date = Date(1999, 5, 31);
   5041             date.add!"months"(-1, AllowDayOverflow.no);
   5042             assert(date == Date(1999, 4, 30));
   5043         }
   5044 
   5045         {
   5046             auto date = Date(1999, 2, 28);
   5047             date.add!"months"(12, AllowDayOverflow.no);
   5048             assert(date == Date(2000, 2, 28));
   5049         }
   5050 
   5051         {
   5052             auto date = Date(2000, 2, 29);
   5053             date.add!"months"(12, AllowDayOverflow.no);
   5054             assert(date == Date(2001, 2, 28));
   5055         }
   5056 
   5057         {
   5058             auto date = Date(1999, 7, 31);
   5059             date.add!"months"(1, AllowDayOverflow.no);
   5060             assert(date == Date(1999, 8, 31));
   5061             date.add!"months"(1, AllowDayOverflow.no);
   5062             assert(date == Date(1999, 9, 30));
   5063         }
   5064 
   5065         {
   5066             auto date = Date(1998, 8, 31);
   5067             date.add!"months"(13, AllowDayOverflow.no);
   5068             assert(date == Date(1999, 9, 30));
   5069             date.add!"months"(-13, AllowDayOverflow.no);
   5070             assert(date == Date(1998, 8, 30));
   5071         }
   5072 
   5073         {
   5074             auto date = Date(1997, 12, 31);
   5075             date.add!"months"(13, AllowDayOverflow.no);
   5076             assert(date == Date(1999, 1, 31));
   5077             date.add!"months"(-13, AllowDayOverflow.no);
   5078             assert(date == Date(1997, 12, 31));
   5079         }
   5080 
   5081         {
   5082             auto date = Date(1997, 12, 31);
   5083             date.add!"months"(14, AllowDayOverflow.no);
   5084             assert(date == Date(1999, 2, 28));
   5085             date.add!"months"(-14, AllowDayOverflow.no);
   5086             assert(date == Date(1997, 12, 28));
   5087         }
   5088 
   5089         {
   5090             auto date = Date(1998, 12, 31);
   5091             date.add!"months"(14, AllowDayOverflow.no);
   5092             assert(date == Date(2000, 2, 29));
   5093             date.add!"months"(-14, AllowDayOverflow.no);
   5094             assert(date == Date(1998, 12, 29));
   5095         }
   5096 
   5097         {
   5098             auto date = Date(1999, 12, 31);
   5099             date.add!"months"(14, AllowDayOverflow.no);
   5100             assert(date == Date(2001, 2, 28));
   5101             date.add!"months"(-14, AllowDayOverflow.no);
   5102             assert(date == Date(1999, 12, 28));
   5103         }
   5104 
   5105         // Test B.C.
   5106         {
   5107             auto date = Date(-1999, 7, 6);
   5108             date.add!"months"(3, AllowDayOverflow.no);
   5109             assert(date == Date(-1999, 10, 6));
   5110             date.add!"months"(-4, AllowDayOverflow.no);
   5111             assert(date == Date(-1999, 6, 6));
   5112         }
   5113 
   5114         {
   5115             auto date = Date(-1999, 7, 6);
   5116             date.add!"months"(6, AllowDayOverflow.no);
   5117             assert(date == Date(-1998, 1, 6));
   5118             date.add!"months"(-6, AllowDayOverflow.no);
   5119             assert(date == Date(-1999, 7, 6));
   5120         }
   5121 
   5122         {
   5123             auto date = Date(-1999, 7, 6);
   5124             date.add!"months"(-27, AllowDayOverflow.no);
   5125             assert(date == Date(-2001, 4, 6));
   5126             date.add!"months"(28, AllowDayOverflow.no);
   5127             assert(date == Date(-1999, 8, 6));
   5128         }
   5129 
   5130         {
   5131             auto date = Date(-1999, 5, 31);
   5132             date.add!"months"(1, AllowDayOverflow.no);
   5133             assert(date == Date(-1999, 6, 30));
   5134         }
   5135 
   5136         {
   5137             auto date = Date(-1999, 5, 31);
   5138             date.add!"months"(-1, AllowDayOverflow.no);
   5139             assert(date == Date(-1999, 4, 30));
   5140         }
   5141 
   5142         {
   5143             auto date = Date(-1999, 2, 28);
   5144             date.add!"months"(-12, AllowDayOverflow.no);
   5145             assert(date == Date(-2000, 2, 28));
   5146         }
   5147 
   5148         {
   5149             auto date = Date(-2000, 2, 29);
   5150             date.add!"months"(-12, AllowDayOverflow.no);
   5151             assert(date == Date(-2001, 2, 28));
   5152         }
   5153 
   5154         {
   5155             auto date = Date(-1999, 7, 31);
   5156             date.add!"months"(1, AllowDayOverflow.no);
   5157             assert(date == Date(-1999, 8, 31));
   5158             date.add!"months"(1, AllowDayOverflow.no);
   5159             assert(date == Date(-1999, 9, 30));
   5160         }
   5161 
   5162         {
   5163             auto date = Date(-1998, 8, 31);
   5164             date.add!"months"(13, AllowDayOverflow.no);
   5165             assert(date == Date(-1997, 9, 30));
   5166             date.add!"months"(-13, AllowDayOverflow.no);
   5167             assert(date == Date(-1998, 8, 30));
   5168         }
   5169 
   5170         {
   5171             auto date = Date(-1997, 12, 31);
   5172             date.add!"months"(13, AllowDayOverflow.no);
   5173             assert(date == Date(-1995, 1, 31));
   5174             date.add!"months"(-13, AllowDayOverflow.no);
   5175             assert(date == Date(-1997, 12, 31));
   5176         }
   5177 
   5178         {
   5179             auto date = Date(-1997, 12, 31);
   5180             date.add!"months"(14, AllowDayOverflow.no);
   5181             assert(date == Date(-1995, 2, 28));
   5182             date.add!"months"(-14, AllowDayOverflow.no);
   5183             assert(date == Date(-1997, 12, 28));
   5184         }
   5185 
   5186         {
   5187             auto date = Date(-2002, 12, 31);
   5188             date.add!"months"(14, AllowDayOverflow.no);
   5189             assert(date == Date(-2000, 2, 29));
   5190             date.add!"months"(-14, AllowDayOverflow.no);
   5191             assert(date == Date(-2002, 12, 29));
   5192         }
   5193 
   5194         {
   5195             auto date = Date(-2001, 12, 31);
   5196             date.add!"months"(14, AllowDayOverflow.no);
   5197             assert(date == Date(-1999, 2, 28));
   5198             date.add!"months"(-14, AllowDayOverflow.no);
   5199             assert(date == Date(-2001, 12, 28));
   5200         }
   5201 
   5202         // Test Both
   5203         {
   5204             auto date = Date(1, 1, 1);
   5205             date.add!"months"(-1, AllowDayOverflow.no);
   5206             assert(date == Date(0, 12, 1));
   5207             date.add!"months"(1, AllowDayOverflow.no);
   5208             assert(date == Date(1, 1, 1));
   5209         }
   5210 
   5211         {
   5212             auto date = Date(4, 1, 1);
   5213             date.add!"months"(-48, AllowDayOverflow.no);
   5214             assert(date == Date(0, 1, 1));
   5215             date.add!"months"(48, AllowDayOverflow.no);
   5216             assert(date == Date(4, 1, 1));
   5217         }
   5218 
   5219         {
   5220             auto date = Date(4, 3, 31);
   5221             date.add!"months"(-49, AllowDayOverflow.no);
   5222             assert(date == Date(0, 2, 29));
   5223             date.add!"months"(49, AllowDayOverflow.no);
   5224             assert(date == Date(4, 3, 29));
   5225         }
   5226 
   5227         {
   5228             auto date = Date(4, 3, 31);
   5229             date.add!"months"(-85, AllowDayOverflow.no);
   5230             assert(date == Date(-3, 2, 28));
   5231             date.add!"months"(85, AllowDayOverflow.no);
   5232             assert(date == Date(4, 3, 28));
   5233         }
   5234 
   5235         {
   5236             auto date = Date(-3, 3, 31);
   5237             date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
   5238             assert(date == Date(-3, 5, 30));
   5239         }
   5240     }
   5241 
   5242 
   5243     /++
   5244         Adds the given number of years or months to this $(LREF Date), mutating
   5245         it. A negative number will subtract.
   5246 
   5247         The difference between rolling and adding is that rolling does not
   5248         affect larger units. Rolling a $(LREF Date) 12 months gets
   5249         the exact same $(LREF Date). However, the days can still be affected due
   5250         to the differing number of days in each month.
   5251 
   5252         Because there are no units larger than years, there is no difference
   5253         between adding and rolling years.
   5254 
   5255         Params:
   5256             units         = The type of units to add ("years" or "months").
   5257             value         = The number of months or years to add to this
   5258                             $(LREF Date).
   5259             allowOverflow = Whether the day should be allowed to overflow,
   5260                             causing the month to increment.
   5261 
   5262         Returns:
   5263             A reference to the `Date` (`this`).
   5264       +/
   5265     @safe pure nothrow @nogc
   5266     ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
   5267         if (units == "years")
   5268     {
   5269         return add!"years"(value, allowOverflow);
   5270     }
   5271 
   5272     ///
   5273     @safe unittest
   5274     {
   5275         auto d1 = Date(2010, 1, 1);
   5276         d1.roll!"months"(1);
   5277         assert(d1 == Date(2010, 2, 1));
   5278 
   5279         auto d2 = Date(2010, 1, 1);
   5280         d2.roll!"months"(-1);
   5281         assert(d2 == Date(2010, 12, 1));
   5282 
   5283         auto d3 = Date(1999, 1, 29);
   5284         d3.roll!"months"(1);
   5285         assert(d3 == Date(1999, 3, 1));
   5286 
   5287         auto d4 = Date(1999, 1, 29);
   5288         d4.roll!"months"(1, AllowDayOverflow.no);
   5289         assert(d4 == Date(1999, 2, 28));
   5290 
   5291         auto d5 = Date(2000, 2, 29);
   5292         d5.roll!"years"(1);
   5293         assert(d5 == Date(2001, 3, 1));
   5294 
   5295         auto d6 = Date(2000, 2, 29);
   5296         d6.roll!"years"(1, AllowDayOverflow.no);
   5297         assert(d6 == Date(2001, 2, 28));
   5298     }
   5299 
   5300     @safe unittest
   5301     {
   5302         const cdate = Date(1999, 7, 6);
   5303         immutable idate = Date(1999, 7, 6);
   5304         static assert(!__traits(compiles, cdate.roll!"years"(3)));
   5305         static assert(!__traits(compiles, idate.rolYears(3)));
   5306     }
   5307 
   5308 
   5309     // Shares documentation with "years" version.
   5310     @safe pure nothrow @nogc
   5311     ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
   5312         if (units == "months")
   5313     {
   5314         months %= 12;
   5315         auto newMonth = _month + months;
   5316 
   5317         if (months < 0)
   5318         {
   5319             if (newMonth < 1)
   5320                 newMonth += 12;
   5321         }
   5322         else
   5323         {
   5324             if (newMonth > 12)
   5325                 newMonth -= 12;
   5326         }
   5327 
   5328         _month = cast(Month) newMonth;
   5329 
   5330         immutable currMaxDay = maxDay(_year, _month);
   5331         immutable overflow = _day - currMaxDay;
   5332 
   5333         if (overflow > 0)
   5334         {
   5335             if (allowOverflow == AllowDayOverflow.yes)
   5336             {
   5337                 ++_month;
   5338                 _day = cast(ubyte) overflow;
   5339             }
   5340             else
   5341                 _day = cast(ubyte) currMaxDay;
   5342         }
   5343 
   5344         return this;
   5345     }
   5346 
   5347     // Test roll!"months"() with AllowDayOverflow.yes
   5348     @safe unittest
   5349     {
   5350         // Test A.D.
   5351         {
   5352             auto date = Date(1999, 7, 6);
   5353             date.roll!"months"(3);
   5354             assert(date == Date(1999, 10, 6));
   5355             date.roll!"months"(-4);
   5356             assert(date == Date(1999, 6, 6));
   5357         }
   5358 
   5359         {
   5360             auto date = Date(1999, 7, 6);
   5361             date.roll!"months"(6);
   5362             assert(date == Date(1999, 1, 6));
   5363             date.roll!"months"(-6);
   5364             assert(date == Date(1999, 7, 6));
   5365         }
   5366 
   5367         {
   5368             auto date = Date(1999, 7, 6);
   5369             date.roll!"months"(27);
   5370             assert(date == Date(1999, 10, 6));
   5371             date.roll!"months"(-28);
   5372             assert(date == Date(1999, 6, 6));
   5373         }
   5374 
   5375         {
   5376             auto date = Date(1999, 5, 31);
   5377             date.roll!"months"(1);
   5378             assert(date == Date(1999, 7, 1));
   5379         }
   5380 
   5381         {
   5382             auto date = Date(1999, 5, 31);
   5383             date.roll!"months"(-1);
   5384             assert(date == Date(1999, 5, 1));
   5385         }
   5386 
   5387         {
   5388             auto date = Date(1999, 2, 28);
   5389             date.roll!"months"(12);
   5390             assert(date == Date(1999, 2, 28));
   5391         }
   5392 
   5393         {
   5394             auto date = Date(2000, 2, 29);
   5395             date.roll!"months"(12);
   5396             assert(date == Date(2000, 2, 29));
   5397         }
   5398 
   5399         {
   5400             auto date = Date(1999, 7, 31);
   5401             date.roll!"months"(1);
   5402             assert(date == Date(1999, 8, 31));
   5403             date.roll!"months"(1);
   5404             assert(date == Date(1999, 10, 1));
   5405         }
   5406 
   5407         {
   5408             auto date = Date(1998, 8, 31);
   5409             date.roll!"months"(13);
   5410             assert(date == Date(1998, 10, 1));
   5411             date.roll!"months"(-13);
   5412             assert(date == Date(1998, 9, 1));
   5413         }
   5414 
   5415         {
   5416             auto date = Date(1997, 12, 31);
   5417             date.roll!"months"(13);
   5418             assert(date == Date(1997, 1, 31));
   5419             date.roll!"months"(-13);
   5420             assert(date == Date(1997, 12, 31));
   5421         }
   5422 
   5423         {
   5424             auto date = Date(1997, 12, 31);
   5425             date.roll!"months"(14);
   5426             assert(date == Date(1997, 3, 3));
   5427             date.roll!"months"(-14);
   5428             assert(date == Date(1997, 1, 3));
   5429         }
   5430 
   5431         {
   5432             auto date = Date(1998, 12, 31);
   5433             date.roll!"months"(14);
   5434             assert(date == Date(1998, 3, 3));
   5435             date.roll!"months"(-14);
   5436             assert(date == Date(1998, 1, 3));
   5437         }
   5438 
   5439         {
   5440             auto date = Date(1999, 12, 31);
   5441             date.roll!"months"(14);
   5442             assert(date == Date(1999, 3, 3));
   5443             date.roll!"months"(-14);
   5444             assert(date == Date(1999, 1, 3));
   5445         }
   5446 
   5447         // Test B.C.
   5448         {
   5449             auto date = Date(-1999, 7, 6);
   5450             date.roll!"months"(3);
   5451             assert(date == Date(-1999, 10, 6));
   5452             date.roll!"months"(-4);
   5453             assert(date == Date(-1999, 6, 6));
   5454         }
   5455 
   5456         {
   5457             auto date = Date(-1999, 7, 6);
   5458             date.roll!"months"(6);
   5459             assert(date == Date(-1999, 1, 6));
   5460             date.roll!"months"(-6);
   5461             assert(date == Date(-1999, 7, 6));
   5462         }
   5463 
   5464         {
   5465             auto date = Date(-1999, 7, 6);
   5466             date.roll!"months"(-27);
   5467             assert(date == Date(-1999, 4, 6));
   5468             date.roll!"months"(28);
   5469             assert(date == Date(-1999, 8, 6));
   5470         }
   5471 
   5472         {
   5473             auto date = Date(-1999, 5, 31);
   5474             date.roll!"months"(1);
   5475             assert(date == Date(-1999, 7, 1));
   5476         }
   5477 
   5478         {
   5479             auto date = Date(-1999, 5, 31);
   5480             date.roll!"months"(-1);
   5481             assert(date == Date(-1999, 5, 1));
   5482         }
   5483 
   5484         {
   5485             auto date = Date(-1999, 2, 28);
   5486             date.roll!"months"(-12);
   5487             assert(date == Date(-1999, 2, 28));
   5488         }
   5489 
   5490         {
   5491             auto date = Date(-2000, 2, 29);
   5492             date.roll!"months"(-12);
   5493             assert(date == Date(-2000, 2, 29));
   5494         }
   5495 
   5496         {
   5497             auto date = Date(-1999, 7, 31);
   5498             date.roll!"months"(1);
   5499             assert(date == Date(-1999, 8, 31));
   5500             date.roll!"months"(1);
   5501             assert(date == Date(-1999, 10, 1));
   5502         }
   5503 
   5504         {
   5505             auto date = Date(-1998, 8, 31);
   5506             date.roll!"months"(13);
   5507             assert(date == Date(-1998, 10, 1));
   5508             date.roll!"months"(-13);
   5509             assert(date == Date(-1998, 9, 1));
   5510         }
   5511 
   5512         {
   5513             auto date = Date(-1997, 12, 31);
   5514             date.roll!"months"(13);
   5515             assert(date == Date(-1997, 1, 31));
   5516             date.roll!"months"(-13);
   5517             assert(date == Date(-1997, 12, 31));
   5518         }
   5519 
   5520         {
   5521             auto date = Date(-1997, 12, 31);
   5522             date.roll!"months"(14);
   5523             assert(date == Date(-1997, 3, 3));
   5524             date.roll!"months"(-14);
   5525             assert(date == Date(-1997, 1, 3));
   5526         }
   5527 
   5528         {
   5529             auto date = Date(-2002, 12, 31);
   5530             date.roll!"months"(14);
   5531             assert(date == Date(-2002, 3, 3));
   5532             date.roll!"months"(-14);
   5533             assert(date == Date(-2002, 1, 3));
   5534         }
   5535 
   5536         {
   5537             auto date = Date(-2001, 12, 31);
   5538             date.roll!"months"(14);
   5539             assert(date == Date(-2001, 3, 3));
   5540             date.roll!"months"(-14);
   5541             assert(date == Date(-2001, 1, 3));
   5542         }
   5543 
   5544         // Test Both
   5545         {
   5546             auto date = Date(1, 1, 1);
   5547             date.roll!"months"(-1);
   5548             assert(date == Date(1, 12, 1));
   5549             date.roll!"months"(1);
   5550             assert(date == Date(1, 1, 1));
   5551         }
   5552 
   5553         {
   5554             auto date = Date(4, 1, 1);
   5555             date.roll!"months"(-48);
   5556             assert(date == Date(4, 1, 1));
   5557             date.roll!"months"(48);
   5558             assert(date == Date(4, 1, 1));
   5559         }
   5560 
   5561         {
   5562             auto date = Date(4, 3, 31);
   5563             date.roll!"months"(-49);
   5564             assert(date == Date(4, 3, 2));
   5565             date.roll!"months"(49);
   5566             assert(date == Date(4, 4, 2));
   5567         }
   5568 
   5569         {
   5570             auto date = Date(4, 3, 31);
   5571             date.roll!"months"(-85);
   5572             assert(date == Date(4, 3, 2));
   5573             date.roll!"months"(85);
   5574             assert(date == Date(4, 4, 2));
   5575         }
   5576 
   5577         {
   5578             auto date = Date(-1, 1, 1);
   5579             date.roll!"months"(-1);
   5580             assert(date == Date(-1, 12, 1));
   5581             date.roll!"months"(1);
   5582             assert(date == Date(-1, 1, 1));
   5583         }
   5584 
   5585         {
   5586             auto date = Date(-4, 1, 1);
   5587             date.roll!"months"(-48);
   5588             assert(date == Date(-4, 1, 1));
   5589             date.roll!"months"(48);
   5590             assert(date == Date(-4, 1, 1));
   5591         }
   5592 
   5593         {
   5594             auto date = Date(-4, 3, 31);
   5595             date.roll!"months"(-49);
   5596             assert(date == Date(-4, 3, 2));
   5597             date.roll!"months"(49);
   5598             assert(date == Date(-4, 4, 2));
   5599         }
   5600 
   5601         {
   5602             auto date = Date(-4, 3, 31);
   5603             date.roll!"months"(-85);
   5604             assert(date == Date(-4, 3, 2));
   5605             date.roll!"months"(85);
   5606             assert(date == Date(-4, 4, 2));
   5607         }
   5608 
   5609         {
   5610             auto date = Date(-3, 3, 31);
   5611             date.roll!"months"(85).roll!"months"(-83);
   5612             assert(date == Date(-3, 6, 1));
   5613         }
   5614 
   5615         const cdate = Date(1999, 7, 6);
   5616         immutable idate = Date(1999, 7, 6);
   5617         static assert(!__traits(compiles, cdate.roll!"months"(3)));
   5618         static assert(!__traits(compiles, idate.roll!"months"(3)));
   5619     }
   5620 
   5621     // Test roll!"months"() with AllowDayOverflow.no
   5622     @safe unittest
   5623     {
   5624         // Test A.D.
   5625         {
   5626             auto date = Date(1999, 7, 6);
   5627             date.roll!"months"(3, AllowDayOverflow.no);
   5628             assert(date == Date(1999, 10, 6));
   5629             date.roll!"months"(-4, AllowDayOverflow.no);
   5630             assert(date == Date(1999, 6, 6));
   5631         }
   5632 
   5633         {
   5634             auto date = Date(1999, 7, 6);
   5635             date.roll!"months"(6, AllowDayOverflow.no);
   5636             assert(date == Date(1999, 1, 6));
   5637             date.roll!"months"(-6, AllowDayOverflow.no);
   5638             assert(date == Date(1999, 7, 6));
   5639         }
   5640 
   5641         {
   5642             auto date = Date(1999, 7, 6);
   5643             date.roll!"months"(27, AllowDayOverflow.no);
   5644             assert(date == Date(1999, 10, 6));
   5645             date.roll!"months"(-28, AllowDayOverflow.no);
   5646             assert(date == Date(1999, 6, 6));
   5647         }
   5648 
   5649         {
   5650             auto date = Date(1999, 5, 31);
   5651             date.roll!"months"(1, AllowDayOverflow.no);
   5652             assert(date == Date(1999, 6, 30));
   5653         }
   5654 
   5655         {
   5656             auto date = Date(1999, 5, 31);
   5657             date.roll!"months"(-1, AllowDayOverflow.no);
   5658             assert(date == Date(1999, 4, 30));
   5659         }
   5660 
   5661         {
   5662             auto date = Date(1999, 2, 28);
   5663             date.roll!"months"(12, AllowDayOverflow.no);
   5664             assert(date == Date(1999, 2, 28));
   5665         }
   5666 
   5667         {
   5668             auto date = Date(2000, 2, 29);
   5669             date.roll!"months"(12, AllowDayOverflow.no);
   5670             assert(date == Date(2000, 2, 29));
   5671         }
   5672 
   5673         {
   5674             auto date = Date(1999, 7, 31);
   5675             date.roll!"months"(1, AllowDayOverflow.no);
   5676             assert(date == Date(1999, 8, 31));
   5677             date.roll!"months"(1, AllowDayOverflow.no);
   5678             assert(date == Date(1999, 9, 30));
   5679         }
   5680 
   5681         {
   5682             auto date = Date(1998, 8, 31);
   5683             date.roll!"months"(13, AllowDayOverflow.no);
   5684             assert(date == Date(1998, 9, 30));
   5685             date.roll!"months"(-13, AllowDayOverflow.no);
   5686             assert(date == Date(1998, 8, 30));
   5687         }
   5688 
   5689         {
   5690             auto date = Date(1997, 12, 31);
   5691             date.roll!"months"(13, AllowDayOverflow.no);
   5692             assert(date == Date(1997, 1, 31));
   5693             date.roll!"months"(-13, AllowDayOverflow.no);
   5694             assert(date == Date(1997, 12, 31));
   5695         }
   5696 
   5697         {
   5698             auto date = Date(1997, 12, 31);
   5699             date.roll!"months"(14, AllowDayOverflow.no);
   5700             assert(date == Date(1997, 2, 28));
   5701             date.roll!"months"(-14, AllowDayOverflow.no);
   5702             assert(date == Date(1997, 12, 28));
   5703         }
   5704 
   5705         {
   5706             auto date = Date(1998, 12, 31);
   5707             date.roll!"months"(14, AllowDayOverflow.no);
   5708             assert(date == Date(1998, 2, 28));
   5709             date.roll!"months"(-14, AllowDayOverflow.no);
   5710             assert(date == Date(1998, 12, 28));
   5711         }
   5712 
   5713         {
   5714             auto date = Date(1999, 12, 31);
   5715             date.roll!"months"(14, AllowDayOverflow.no);
   5716             assert(date == Date(1999, 2, 28));
   5717             date.roll!"months"(-14, AllowDayOverflow.no);
   5718             assert(date == Date(1999, 12, 28));
   5719         }
   5720 
   5721         // Test B.C.
   5722         {
   5723             auto date = Date(-1999, 7, 6);
   5724             date.roll!"months"(3, AllowDayOverflow.no);
   5725             assert(date == Date(-1999, 10, 6));
   5726             date.roll!"months"(-4, AllowDayOverflow.no);
   5727             assert(date == Date(-1999, 6, 6));
   5728         }
   5729 
   5730         {
   5731             auto date = Date(-1999, 7, 6);
   5732             date.roll!"months"(6, AllowDayOverflow.no);
   5733             assert(date == Date(-1999, 1, 6));
   5734             date.roll!"months"(-6, AllowDayOverflow.no);
   5735             assert(date == Date(-1999, 7, 6));
   5736         }
   5737 
   5738         {
   5739             auto date = Date(-1999, 7, 6);
   5740             date.roll!"months"(-27, AllowDayOverflow.no);
   5741             assert(date == Date(-1999, 4, 6));
   5742             date.roll!"months"(28, AllowDayOverflow.no);
   5743             assert(date == Date(-1999, 8, 6));
   5744         }
   5745 
   5746         {
   5747             auto date = Date(-1999, 5, 31);
   5748             date.roll!"months"(1, AllowDayOverflow.no);
   5749             assert(date == Date(-1999, 6, 30));
   5750         }
   5751 
   5752         {
   5753             auto date = Date(-1999, 5, 31);
   5754             date.roll!"months"(-1, AllowDayOverflow.no);
   5755             assert(date == Date(-1999, 4, 30));
   5756         }
   5757 
   5758         {
   5759             auto date = Date(-1999, 2, 28);
   5760             date.roll!"months"(-12, AllowDayOverflow.no);
   5761             assert(date == Date(-1999, 2, 28));
   5762         }
   5763 
   5764         {
   5765             auto date = Date(-2000, 2, 29);
   5766             date.roll!"months"(-12, AllowDayOverflow.no);
   5767             assert(date == Date(-2000, 2, 29));
   5768         }
   5769 
   5770         {
   5771             auto date = Date(-1999, 7, 31);
   5772             date.roll!"months"(1, AllowDayOverflow.no);
   5773             assert(date == Date(-1999, 8, 31));
   5774             date.roll!"months"(1, AllowDayOverflow.no);
   5775             assert(date == Date(-1999, 9, 30));
   5776         }
   5777 
   5778         {
   5779             auto date = Date(-1998, 8, 31);
   5780             date.roll!"months"(13, AllowDayOverflow.no);
   5781             assert(date == Date(-1998, 9, 30));
   5782             date.roll!"months"(-13, AllowDayOverflow.no);
   5783             assert(date == Date(-1998, 8, 30));
   5784         }
   5785 
   5786         {
   5787             auto date = Date(-1997, 12, 31);
   5788             date.roll!"months"(13, AllowDayOverflow.no);
   5789             assert(date == Date(-1997, 1, 31));
   5790             date.roll!"months"(-13, AllowDayOverflow.no);
   5791             assert(date == Date(-1997, 12, 31));
   5792         }
   5793 
   5794         {
   5795             auto date = Date(-1997, 12, 31);
   5796             date.roll!"months"(14, AllowDayOverflow.no);
   5797             assert(date == Date(-1997, 2, 28));
   5798             date.roll!"months"(-14, AllowDayOverflow.no);
   5799             assert(date == Date(-1997, 12, 28));
   5800         }
   5801 
   5802         {
   5803             auto date = Date(-2002, 12, 31);
   5804             date.roll!"months"(14, AllowDayOverflow.no);
   5805             assert(date == Date(-2002, 2, 28));
   5806             date.roll!"months"(-14, AllowDayOverflow.no);
   5807             assert(date == Date(-2002, 12, 28));
   5808         }
   5809 
   5810         {
   5811             auto date = Date(-2001, 12, 31);
   5812             date.roll!"months"(14, AllowDayOverflow.no);
   5813             assert(date == Date(-2001, 2, 28));
   5814             date.roll!"months"(-14, AllowDayOverflow.no);
   5815             assert(date == Date(-2001, 12, 28));
   5816         }
   5817 
   5818         // Test Both
   5819         {
   5820             auto date = Date(1, 1, 1);
   5821             date.roll!"months"(-1, AllowDayOverflow.no);
   5822             assert(date == Date(1, 12, 1));
   5823             date.roll!"months"(1, AllowDayOverflow.no);
   5824             assert(date == Date(1, 1, 1));
   5825         }
   5826 
   5827         {
   5828             auto date = Date(4, 1, 1);
   5829             date.roll!"months"(-48, AllowDayOverflow.no);
   5830             assert(date == Date(4, 1, 1));
   5831             date.roll!"months"(48, AllowDayOverflow.no);
   5832             assert(date == Date(4, 1, 1));
   5833         }
   5834 
   5835         {
   5836             auto date = Date(4, 3, 31);
   5837             date.roll!"months"(-49, AllowDayOverflow.no);
   5838             assert(date == Date(4, 2, 29));
   5839             date.roll!"months"(49, AllowDayOverflow.no);
   5840             assert(date == Date(4, 3, 29));
   5841         }
   5842 
   5843         {
   5844             auto date = Date(4, 3, 31);
   5845             date.roll!"months"(-85, AllowDayOverflow.no);
   5846             assert(date == Date(4, 2, 29));
   5847             date.roll!"months"(85, AllowDayOverflow.no);
   5848             assert(date == Date(4, 3, 29));
   5849         }
   5850 
   5851         {
   5852             auto date = Date(-1, 1, 1);
   5853             date.roll!"months"(-1, AllowDayOverflow.no);
   5854             assert(date == Date(-1, 12, 1));
   5855             date.roll!"months"(1, AllowDayOverflow.no);
   5856             assert(date == Date(-1, 1, 1));
   5857         }
   5858 
   5859         {
   5860             auto date = Date(-4, 1, 1);
   5861             date.roll!"months"(-48, AllowDayOverflow.no);
   5862             assert(date == Date(-4, 1, 1));
   5863             date.roll!"months"(48, AllowDayOverflow.no);
   5864             assert(date == Date(-4, 1, 1));
   5865         }
   5866 
   5867         {
   5868             auto date = Date(-4, 3, 31);
   5869             date.roll!"months"(-49, AllowDayOverflow.no);
   5870             assert(date == Date(-4, 2, 29));
   5871             date.roll!"months"(49, AllowDayOverflow.no);
   5872             assert(date == Date(-4, 3, 29));
   5873         }
   5874 
   5875         {
   5876             auto date = Date(-4, 3, 31);
   5877             date.roll!"months"(-85, AllowDayOverflow.no);
   5878             assert(date == Date(-4, 2, 29));
   5879             date.roll!"months"(85, AllowDayOverflow.no);
   5880             assert(date == Date(-4, 3, 29));
   5881         }
   5882 
   5883         {
   5884             auto date = Date(-3, 3, 31);
   5885             date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
   5886             assert(date == Date(-3, 5, 30));
   5887         }
   5888     }
   5889 
   5890 
   5891     /++
   5892         Adds the given number of units to this $(LREF Date), mutating it. A
   5893         negative number will subtract.
   5894 
   5895         The difference between rolling and adding is that rolling does not
   5896         affect larger units. For instance, rolling a $(LREF Date) one
   5897         year's worth of days gets the exact same $(LREF Date).
   5898 
   5899         The only accepted units are `"days"`.
   5900 
   5901         Params:
   5902             units = The units to add. Must be `"days"`.
   5903             days  = The number of days to add to this $(LREF Date).
   5904 
   5905         Returns:
   5906             A reference to the `Date` (`this`).
   5907       +/
   5908     ref Date roll(string units)(long days) @safe pure nothrow @nogc
   5909         if (units == "days")
   5910     {
   5911         immutable limit = maxDay(_year, _month);
   5912         days %= limit;
   5913         auto newDay = _day + days;
   5914 
   5915         if (days < 0)
   5916         {
   5917             if (newDay < 1)
   5918                 newDay += limit;
   5919         }
   5920         else if (newDay > limit)
   5921             newDay -= limit;
   5922 
   5923         _day = cast(ubyte) newDay;
   5924         return this;
   5925     }
   5926 
   5927     ///
   5928     @safe unittest
   5929     {
   5930         auto d = Date(2010, 1, 1);
   5931         d.roll!"days"(1);
   5932         assert(d == Date(2010, 1, 2));
   5933         d.roll!"days"(365);
   5934         assert(d == Date(2010, 1, 26));
   5935         d.roll!"days"(-32);
   5936         assert(d == Date(2010, 1, 25));
   5937     }
   5938 
   5939     @safe unittest
   5940     {
   5941         // Test A.D.
   5942         {
   5943             auto date = Date(1999, 2, 28);
   5944             date.roll!"days"(1);
   5945             assert(date == Date(1999, 2, 1));
   5946             date.roll!"days"(-1);
   5947             assert(date == Date(1999, 2, 28));
   5948         }
   5949 
   5950         {
   5951             auto date = Date(2000, 2, 28);
   5952             date.roll!"days"(1);
   5953             assert(date == Date(2000, 2, 29));
   5954             date.roll!"days"(1);
   5955             assert(date == Date(2000, 2, 1));
   5956             date.roll!"days"(-1);
   5957             assert(date == Date(2000, 2, 29));
   5958         }
   5959 
   5960         {
   5961             auto date = Date(1999, 6, 30);
   5962             date.roll!"days"(1);
   5963             assert(date == Date(1999, 6, 1));
   5964             date.roll!"days"(-1);
   5965             assert(date == Date(1999, 6, 30));
   5966         }
   5967 
   5968         {
   5969             auto date = Date(1999, 7, 31);
   5970             date.roll!"days"(1);
   5971             assert(date == Date(1999, 7, 1));
   5972             date.roll!"days"(-1);
   5973             assert(date == Date(1999, 7, 31));
   5974         }
   5975 
   5976         {
   5977             auto date = Date(1999, 1, 1);
   5978             date.roll!"days"(-1);
   5979             assert(date == Date(1999, 1, 31));
   5980             date.roll!"days"(1);
   5981             assert(date == Date(1999, 1, 1));
   5982         }
   5983 
   5984         {
   5985             auto date = Date(1999, 7, 6);
   5986             date.roll!"days"(9);
   5987             assert(date == Date(1999, 7, 15));
   5988             date.roll!"days"(-11);
   5989             assert(date == Date(1999, 7, 4));
   5990             date.roll!"days"(30);
   5991             assert(date == Date(1999, 7, 3));
   5992             date.roll!"days"(-3);
   5993             assert(date == Date(1999, 7, 31));
   5994         }
   5995 
   5996         {
   5997             auto date = Date(1999, 7, 6);
   5998             date.roll!"days"(365);
   5999             assert(date == Date(1999, 7, 30));
   6000             date.roll!"days"(-365);
   6001             assert(date == Date(1999, 7, 6));
   6002             date.roll!"days"(366);
   6003             assert(date == Date(1999, 7, 31));
   6004             date.roll!"days"(730);
   6005             assert(date == Date(1999, 7, 17));
   6006             date.roll!"days"(-1096);
   6007             assert(date == Date(1999, 7, 6));
   6008         }
   6009 
   6010         {
   6011             auto date = Date(1999, 2, 6);
   6012             date.roll!"days"(365);
   6013             assert(date == Date(1999, 2, 7));
   6014             date.roll!"days"(-365);
   6015             assert(date == Date(1999, 2, 6));
   6016             date.roll!"days"(366);
   6017             assert(date == Date(1999, 2, 8));
   6018             date.roll!"days"(730);
   6019             assert(date == Date(1999, 2, 10));
   6020             date.roll!"days"(-1096);
   6021             assert(date == Date(1999, 2, 6));
   6022         }
   6023 
   6024         // Test B.C.
   6025         {
   6026             auto date = Date(-1999, 2, 28);
   6027             date.roll!"days"(1);
   6028             assert(date == Date(-1999, 2, 1));
   6029             date.roll!"days"(-1);
   6030             assert(date == Date(-1999, 2, 28));
   6031         }
   6032 
   6033         {
   6034             auto date = Date(-2000, 2, 28);
   6035             date.roll!"days"(1);
   6036             assert(date == Date(-2000, 2, 29));
   6037             date.roll!"days"(1);
   6038             assert(date == Date(-2000, 2, 1));
   6039             date.roll!"days"(-1);
   6040             assert(date == Date(-2000, 2, 29));
   6041         }
   6042 
   6043         {
   6044             auto date = Date(-1999, 6, 30);
   6045             date.roll!"days"(1);
   6046             assert(date == Date(-1999, 6, 1));
   6047             date.roll!"days"(-1);
   6048             assert(date == Date(-1999, 6, 30));
   6049         }
   6050 
   6051         {
   6052             auto date = Date(-1999, 7, 31);
   6053             date.roll!"days"(1);
   6054             assert(date == Date(-1999, 7, 1));
   6055             date.roll!"days"(-1);
   6056             assert(date == Date(-1999, 7, 31));
   6057         }
   6058 
   6059         {
   6060             auto date = Date(-1999, 1, 1);
   6061             date.roll!"days"(-1);
   6062             assert(date == Date(-1999, 1, 31));
   6063             date.roll!"days"(1);
   6064             assert(date == Date(-1999, 1, 1));
   6065         }
   6066 
   6067         {
   6068             auto date = Date(-1999, 7, 6);
   6069             date.roll!"days"(9);
   6070             assert(date == Date(-1999, 7, 15));
   6071             date.roll!"days"(-11);
   6072             assert(date == Date(-1999, 7, 4));
   6073             date.roll!"days"(30);
   6074             assert(date == Date(-1999, 7, 3));
   6075             date.roll!"days"(-3);
   6076             assert(date == Date(-1999, 7, 31));
   6077         }
   6078 
   6079         {
   6080             auto date = Date(-1999, 7, 6);
   6081             date.roll!"days"(365);
   6082             assert(date == Date(-1999, 7, 30));
   6083             date.roll!"days"(-365);
   6084             assert(date == Date(-1999, 7, 6));
   6085             date.roll!"days"(366);
   6086             assert(date == Date(-1999, 7, 31));
   6087             date.roll!"days"(730);
   6088             assert(date == Date(-1999, 7, 17));
   6089             date.roll!"days"(-1096);
   6090             assert(date == Date(-1999, 7, 6));
   6091         }
   6092 
   6093         // Test Both
   6094         {
   6095             auto date = Date(1, 7, 6);
   6096             date.roll!"days"(-365);
   6097             assert(date == Date(1, 7, 13));
   6098             date.roll!"days"(365);
   6099             assert(date == Date(1, 7, 6));
   6100             date.roll!"days"(-731);
   6101             assert(date == Date(1, 7, 19));
   6102             date.roll!"days"(730);
   6103             assert(date == Date(1, 7, 5));
   6104         }
   6105 
   6106         {
   6107             auto date = Date(0, 7, 6);
   6108             date.roll!"days"(-365);
   6109             assert(date == Date(0, 7, 13));
   6110             date.roll!"days"(365);
   6111             assert(date == Date(0, 7, 6));
   6112             date.roll!"days"(-731);
   6113             assert(date == Date(0, 7, 19));
   6114             date.roll!"days"(730);
   6115             assert(date == Date(0, 7, 5));
   6116         }
   6117 
   6118         {
   6119             auto date = Date(0, 7, 6);
   6120             date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
   6121             assert(date == Date(0, 7, 8));
   6122         }
   6123 
   6124         const cdate = Date(1999, 7, 6);
   6125         immutable idate = Date(1999, 7, 6);
   6126         static assert(!__traits(compiles, cdate.roll!"days"(12)));
   6127         static assert(!__traits(compiles, idate.roll!"days"(12)));
   6128     }
   6129 
   6130     import core.time : Duration;
   6131     /++
   6132         Gives the result of adding or subtracting a $(REF Duration, core,time)
   6133         from
   6134 
   6135         The legal types of arithmetic for $(LREF Date) using this operator are
   6136 
   6137         $(BOOKTABLE,
   6138         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
   6139         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
   6140         )
   6141 
   6142         Params:
   6143             duration = The $(REF Duration, core,time) to add to or subtract from
   6144                        this $(LREF Date).
   6145       +/
   6146     Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
   6147         if (op == "+" || op == "-")
   6148     {
   6149         Date retval = this;
   6150         immutable days = duration.total!"days";
   6151         mixin("return retval._addDays(" ~ op ~ "days);");
   6152     }
   6153 
   6154     ///
   6155     @safe unittest
   6156     {
   6157         import core.time : days;
   6158 
   6159         assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
   6160         assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
   6161 
   6162         assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
   6163         assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
   6164     }
   6165 
   6166     @safe unittest
   6167     {
   6168         auto date = Date(1999, 7, 6);
   6169 
   6170         import core.time : dur;
   6171         assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
   6172         assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
   6173         assert(date + dur!"days"(7) == Date(1999, 7, 13));
   6174         assert(date + dur!"days"(-7) == Date(1999, 6, 29));
   6175 
   6176         assert(date + dur!"hours"(24) == Date(1999, 7, 7));
   6177         assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
   6178         assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
   6179         assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
   6180         assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
   6181         assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
   6182         assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
   6183         assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
   6184         assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
   6185         assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
   6186         assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
   6187         assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
   6188 
   6189         assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
   6190         assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
   6191         assert(date - dur!"days"(-7) == Date(1999, 7, 13));
   6192         assert(date - dur!"days"(7) == Date(1999, 6, 29));
   6193 
   6194         assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
   6195         assert(date - dur!"hours"(24) == Date(1999, 7, 5));
   6196         assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
   6197         assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
   6198         assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
   6199         assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
   6200         assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
   6201         assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
   6202         assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
   6203         assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
   6204         assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
   6205         assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
   6206 
   6207         auto duration = dur!"days"(12);
   6208         const cdate = Date(1999, 7, 6);
   6209         immutable idate = Date(1999, 7, 6);
   6210         assert(date + duration == Date(1999, 7, 18));
   6211         assert(cdate + duration == Date(1999, 7, 18));
   6212         assert(idate + duration == Date(1999, 7, 18));
   6213 
   6214         assert(date - duration == Date(1999, 6, 24));
   6215         assert(cdate - duration == Date(1999, 6, 24));
   6216         assert(idate - duration == Date(1999, 6, 24));
   6217     }
   6218 
   6219 
   6220     /++
   6221         Gives the result of adding or subtracting a $(REF Duration, core,time)
   6222         from this $(LREF Date), as well as assigning the result to this
   6223         $(LREF Date).
   6224 
   6225         The legal types of arithmetic for $(LREF Date) using this operator are
   6226 
   6227         $(BOOKTABLE,
   6228         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
   6229         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
   6230         )
   6231 
   6232         Params:
   6233             duration = The $(REF Duration, core,time) to add to or subtract from
   6234                        this $(LREF Date).
   6235       +/
   6236     ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
   6237         if (op == "+" || op == "-")
   6238     {
   6239         immutable days = duration.total!"days";
   6240         mixin("return _addDays(" ~ op ~ "days);");
   6241     }
   6242 
   6243     @safe unittest
   6244     {
   6245         import core.time : dur;
   6246         assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
   6247         assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
   6248         assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
   6249         assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
   6250 
   6251         assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
   6252         assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
   6253         assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
   6254         assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
   6255         assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
   6256         assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
   6257         assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
   6258         assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
   6259         assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
   6260         assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
   6261         assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
   6262         assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
   6263 
   6264         assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
   6265         assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
   6266         assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
   6267         assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
   6268 
   6269         assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
   6270         assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
   6271         assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
   6272         assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
   6273         assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
   6274         assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
   6275         assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
   6276         assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
   6277         assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
   6278         assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
   6279         assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
   6280         assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
   6281 
   6282         {
   6283             auto date = Date(0, 1, 31);
   6284             (date += dur!"days"(507)) += dur!"days"(-2);
   6285             assert(date == Date(1, 6, 19));
   6286         }
   6287 
   6288         auto duration = dur!"days"(12);
   6289         auto date = Date(1999, 7, 6);
   6290         const cdate = Date(1999, 7, 6);
   6291         immutable idate = Date(1999, 7, 6);
   6292         date += duration;
   6293         static assert(!__traits(compiles, cdate += duration));
   6294         static assert(!__traits(compiles, idate += duration));
   6295 
   6296         date -= duration;
   6297         static assert(!__traits(compiles, cdate -= duration));
   6298         static assert(!__traits(compiles, idate -= duration));
   6299     }
   6300 
   6301 
   6302     /++
   6303         Gives the difference between two $(LREF Date)s.
   6304 
   6305         The legal types of arithmetic for $(LREF Date) using this operator are
   6306 
   6307         $(BOOKTABLE,
   6308         $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
   6309         )
   6310       +/
   6311     Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
   6312         if (op == "-")
   6313     {
   6314         import core.time : dur;
   6315         return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
   6316     }
   6317 
   6318     @safe unittest
   6319     {
   6320         auto date = Date(1999, 7, 6);
   6321 
   6322         import core.time : dur;
   6323         assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
   6324         assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
   6325         assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
   6326         assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
   6327         assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
   6328         assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
   6329 
   6330         const cdate = Date(1999, 7, 6);
   6331         immutable idate = Date(1999, 7, 6);
   6332         assert(date - date == Duration.zero);
   6333         assert(cdate - date == Duration.zero);
   6334         assert(idate - date == Duration.zero);
   6335 
   6336         assert(date - cdate == Duration.zero);
   6337         assert(cdate - cdate == Duration.zero);
   6338         assert(idate - cdate == Duration.zero);
   6339 
   6340         assert(date - idate == Duration.zero);
   6341         assert(cdate - idate == Duration.zero);
   6342         assert(idate - idate == Duration.zero);
   6343     }
   6344 
   6345 
   6346     /++
   6347         Returns the difference between the two $(LREF Date)s in months.
   6348 
   6349         To get the difference in years, subtract the year property
   6350         of two $(LREF Date)s. To get the difference in days or weeks,
   6351         subtract the $(LREF Date)s themselves and use the
   6352         $(REF Duration, core,time) that results. Because converting between
   6353         months and smaller units requires a specific date (which
   6354         $(REF Duration, core,time)s don't have), getting the difference in
   6355         months requires some math using both the year and month properties, so
   6356         this is a convenience function for getting the difference in months.
   6357 
   6358         Note that the number of days in the months or how far into the month
   6359         either $(LREF Date) is is irrelevant. It is the difference in the month
   6360         property combined with the difference in years * 12. So, for instance,
   6361         December 31st and January 1st are one month apart just as December 1st
   6362         and January 31st are one month apart.
   6363 
   6364         Params:
   6365             rhs = The $(LREF Date) to subtract from this one.
   6366       +/
   6367     int diffMonths(Date rhs) const @safe pure nothrow @nogc
   6368     {
   6369         immutable yearDiff = _year - rhs._year;
   6370         immutable monthDiff = _month - rhs._month;
   6371 
   6372         return yearDiff * 12 + monthDiff;
   6373     }
   6374 
   6375     ///
   6376     @safe unittest
   6377     {
   6378         assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
   6379         assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
   6380         assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
   6381         assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
   6382     }
   6383 
   6384     @safe unittest
   6385     {
   6386         auto date = Date(1999, 7, 6);
   6387 
   6388         // Test A.D.
   6389         assert(date.diffMonths(Date(1998, 6, 5)) == 13);
   6390         assert(date.diffMonths(Date(1998, 7, 5)) == 12);
   6391         assert(date.diffMonths(Date(1998, 8, 5)) == 11);
   6392         assert(date.diffMonths(Date(1998, 9, 5)) == 10);
   6393         assert(date.diffMonths(Date(1998, 10, 5)) == 9);
   6394         assert(date.diffMonths(Date(1998, 11, 5)) == 8);
   6395         assert(date.diffMonths(Date(1998, 12, 5)) == 7);
   6396         assert(date.diffMonths(Date(1999, 1, 5)) == 6);
   6397         assert(date.diffMonths(Date(1999, 2, 6)) == 5);
   6398         assert(date.diffMonths(Date(1999, 3, 6)) == 4);
   6399         assert(date.diffMonths(Date(1999, 4, 6)) == 3);
   6400         assert(date.diffMonths(Date(1999, 5, 6)) == 2);
   6401         assert(date.diffMonths(Date(1999, 6, 6)) == 1);
   6402         assert(date.diffMonths(date) == 0);
   6403         assert(date.diffMonths(Date(1999, 8, 6)) == -1);
   6404         assert(date.diffMonths(Date(1999, 9, 6)) == -2);
   6405         assert(date.diffMonths(Date(1999, 10, 6)) == -3);
   6406         assert(date.diffMonths(Date(1999, 11, 6)) == -4);
   6407         assert(date.diffMonths(Date(1999, 12, 6)) == -5);
   6408         assert(date.diffMonths(Date(2000, 1, 6)) == -6);
   6409         assert(date.diffMonths(Date(2000, 2, 6)) == -7);
   6410         assert(date.diffMonths(Date(2000, 3, 6)) == -8);
   6411         assert(date.diffMonths(Date(2000, 4, 6)) == -9);
   6412         assert(date.diffMonths(Date(2000, 5, 6)) == -10);
   6413         assert(date.diffMonths(Date(2000, 6, 6)) == -11);
   6414         assert(date.diffMonths(Date(2000, 7, 6)) == -12);
   6415         assert(date.diffMonths(Date(2000, 8, 6)) == -13);
   6416 
   6417         assert(Date(1998, 6, 5).diffMonths(date) == -13);
   6418         assert(Date(1998, 7, 5).diffMonths(date) == -12);
   6419         assert(Date(1998, 8, 5).diffMonths(date) == -11);
   6420         assert(Date(1998, 9, 5).diffMonths(date) == -10);
   6421         assert(Date(1998, 10, 5).diffMonths(date) == -9);
   6422         assert(Date(1998, 11, 5).diffMonths(date) == -8);
   6423         assert(Date(1998, 12, 5).diffMonths(date) == -7);
   6424         assert(Date(1999, 1, 5).diffMonths(date) == -6);
   6425         assert(Date(1999, 2, 6).diffMonths(date) == -5);
   6426         assert(Date(1999, 3, 6).diffMonths(date) == -4);
   6427         assert(Date(1999, 4, 6).diffMonths(date) == -3);
   6428         assert(Date(1999, 5, 6).diffMonths(date) == -2);
   6429         assert(Date(1999, 6, 6).diffMonths(date) == -1);
   6430         assert(Date(1999, 8, 6).diffMonths(date) == 1);
   6431         assert(Date(1999, 9, 6).diffMonths(date) == 2);
   6432         assert(Date(1999, 10, 6).diffMonths(date) == 3);
   6433         assert(Date(1999, 11, 6).diffMonths(date) == 4);
   6434         assert(Date(1999, 12, 6).diffMonths(date) == 5);
   6435         assert(Date(2000, 1, 6).diffMonths(date) == 6);
   6436         assert(Date(2000, 2, 6).diffMonths(date) == 7);
   6437         assert(Date(2000, 3, 6).diffMonths(date) == 8);
   6438         assert(Date(2000, 4, 6).diffMonths(date) == 9);
   6439         assert(Date(2000, 5, 6).diffMonths(date) == 10);
   6440         assert(Date(2000, 6, 6).diffMonths(date) == 11);
   6441         assert(Date(2000, 7, 6).diffMonths(date) == 12);
   6442         assert(Date(2000, 8, 6).diffMonths(date) == 13);
   6443 
   6444         assert(date.diffMonths(Date(1999, 6, 30)) == 1);
   6445         assert(date.diffMonths(Date(1999, 7, 1)) == 0);
   6446         assert(date.diffMonths(Date(1999, 7, 6)) == 0);
   6447         assert(date.diffMonths(Date(1999, 7, 11)) == 0);
   6448         assert(date.diffMonths(Date(1999, 7, 16)) == 0);
   6449         assert(date.diffMonths(Date(1999, 7, 21)) == 0);
   6450         assert(date.diffMonths(Date(1999, 7, 26)) == 0);
   6451         assert(date.diffMonths(Date(1999, 7, 31)) == 0);
   6452         assert(date.diffMonths(Date(1999, 8, 1)) == -1);
   6453 
   6454         assert(date.diffMonths(Date(1990, 6, 30)) == 109);
   6455         assert(date.diffMonths(Date(1990, 7, 1)) == 108);
   6456         assert(date.diffMonths(Date(1990, 7, 6)) == 108);
   6457         assert(date.diffMonths(Date(1990, 7, 11)) == 108);
   6458         assert(date.diffMonths(Date(1990, 7, 16)) == 108);
   6459         assert(date.diffMonths(Date(1990, 7, 21)) == 108);
   6460         assert(date.diffMonths(Date(1990, 7, 26)) == 108);
   6461         assert(date.diffMonths(Date(1990, 7, 31)) == 108);
   6462         assert(date.diffMonths(Date(1990, 8, 1)) == 107);
   6463 
   6464         assert(Date(1999, 6, 30).diffMonths(date) == -1);
   6465         assert(Date(1999, 7, 1).diffMonths(date) == 0);
   6466         assert(Date(1999, 7, 6).diffMonths(date) == 0);
   6467         assert(Date(1999, 7, 11).diffMonths(date) == 0);
   6468         assert(Date(1999, 7, 16).diffMonths(date) == 0);
   6469         assert(Date(1999, 7, 21).diffMonths(date) == 0);
   6470         assert(Date(1999, 7, 26).diffMonths(date) == 0);
   6471         assert(Date(1999, 7, 31).diffMonths(date) == 0);
   6472         assert(Date(1999, 8, 1).diffMonths(date) == 1);
   6473 
   6474         assert(Date(1990, 6, 30).diffMonths(date) == -109);
   6475         assert(Date(1990, 7, 1).diffMonths(date) == -108);
   6476         assert(Date(1990, 7, 6).diffMonths(date) == -108);
   6477         assert(Date(1990, 7, 11).diffMonths(date) == -108);
   6478         assert(Date(1990, 7, 16).diffMonths(date) == -108);
   6479         assert(Date(1990, 7, 21).diffMonths(date) == -108);
   6480         assert(Date(1990, 7, 26).diffMonths(date) == -108);
   6481         assert(Date(1990, 7, 31).diffMonths(date) == -108);
   6482         assert(Date(1990, 8, 1).diffMonths(date) == -107);
   6483 
   6484         // Test B.C.
   6485         auto dateBC = Date(-1999, 7, 6);
   6486 
   6487         assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
   6488         assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
   6489         assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
   6490         assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
   6491         assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
   6492         assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
   6493         assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
   6494         assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
   6495         assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
   6496         assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
   6497         assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
   6498         assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
   6499         assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
   6500         assert(dateBC.diffMonths(dateBC) == 0);
   6501         assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
   6502         assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
   6503         assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
   6504         assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
   6505         assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
   6506         assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
   6507         assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
   6508         assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
   6509         assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
   6510         assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
   6511         assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
   6512         assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
   6513         assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
   6514 
   6515         assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
   6516         assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
   6517         assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
   6518         assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
   6519         assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
   6520         assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
   6521         assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
   6522         assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
   6523         assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
   6524         assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
   6525         assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
   6526         assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
   6527         assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
   6528         assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
   6529         assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
   6530         assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
   6531         assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
   6532         assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
   6533         assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
   6534         assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
   6535         assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
   6536         assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
   6537         assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
   6538         assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
   6539         assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
   6540         assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
   6541 
   6542         assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
   6543         assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
   6544         assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
   6545         assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
   6546         assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
   6547         assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
   6548         assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
   6549         assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
   6550         assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
   6551 
   6552         assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
   6553         assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
   6554         assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
   6555         assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
   6556         assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
   6557         assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
   6558         assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
   6559         assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
   6560         assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
   6561 
   6562         assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
   6563         assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
   6564         assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
   6565         assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
   6566         assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
   6567         assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
   6568         assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
   6569         assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
   6570         assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
   6571 
   6572         assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
   6573         assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
   6574         assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
   6575         assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
   6576         assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
   6577         assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
   6578         assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
   6579         assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
   6580         assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
   6581 
   6582         // Test Both
   6583         assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
   6584         assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
   6585 
   6586         const cdate = Date(1999, 7, 6);
   6587         immutable idate = Date(1999, 7, 6);
   6588         assert(date.diffMonths(date) == 0);
   6589         assert(cdate.diffMonths(date) == 0);
   6590         assert(idate.diffMonths(date) == 0);
   6591 
   6592         assert(date.diffMonths(cdate) == 0);
   6593         assert(cdate.diffMonths(cdate) == 0);
   6594         assert(idate.diffMonths(cdate) == 0);
   6595 
   6596         assert(date.diffMonths(idate) == 0);
   6597         assert(cdate.diffMonths(idate) == 0);
   6598         assert(idate.diffMonths(idate) == 0);
   6599     }
   6600 
   6601 
   6602     /++
   6603         Whether this $(LREF Date) is in a leap year.
   6604      +/
   6605     @property bool isLeapYear() const @safe pure nothrow @nogc
   6606     {
   6607         return yearIsLeapYear(_year);
   6608     }
   6609 
   6610     @safe unittest
   6611     {
   6612         auto date = Date(1999, 7, 6);
   6613         const cdate = Date(1999, 7, 6);
   6614         immutable idate = Date(1999, 7, 6);
   6615         static assert(!__traits(compiles, date.isLeapYear = true));
   6616         static assert(!__traits(compiles, cdate.isLeapYear = true));
   6617         static assert(!__traits(compiles, idate.isLeapYear = true));
   6618     }
   6619 
   6620 
   6621     /++
   6622         Day of the week this $(LREF Date) is on.
   6623       +/
   6624     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
   6625     {
   6626         return getDayOfWeek(dayOfGregorianCal);
   6627     }
   6628 
   6629     @safe unittest
   6630     {
   6631         const cdate = Date(1999, 7, 6);
   6632         immutable idate = Date(1999, 7, 6);
   6633         assert(cdate.dayOfWeek == DayOfWeek.tue);
   6634         static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
   6635         assert(idate.dayOfWeek == DayOfWeek.tue);
   6636         static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
   6637     }
   6638 
   6639 
   6640     /++
   6641         Day of the year this $(LREF Date) is on.
   6642       +/
   6643     @property ushort dayOfYear() const @safe pure nothrow @nogc
   6644     {
   6645         if (_month >= Month.jan && _month <= Month.dec)
   6646         {
   6647             immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
   6648             auto monthIndex = _month - Month.jan;
   6649 
   6650             return cast(ushort)(lastDay[monthIndex] + _day);
   6651         }
   6652         assert(0, "Invalid month.");
   6653     }
   6654 
   6655     ///
   6656     @safe unittest
   6657     {
   6658         assert(Date(1999, 1, 1).dayOfYear == 1);
   6659         assert(Date(1999, 12, 31).dayOfYear == 365);
   6660         assert(Date(2000, 12, 31).dayOfYear == 366);
   6661     }
   6662 
   6663     @safe unittest
   6664     {
   6665         import std.algorithm.iteration : filter;
   6666         import std.range : chain;
   6667 
   6668         foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
   6669         {
   6670             foreach (doy; testDaysOfYear)
   6671                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
   6672         }
   6673 
   6674         foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
   6675         {
   6676             foreach (doy; testDaysOfLeapYear)
   6677                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
   6678         }
   6679 
   6680         const cdate = Date(1999, 7, 6);
   6681         immutable idate = Date(1999, 7, 6);
   6682         assert(cdate.dayOfYear == 187);
   6683         assert(idate.dayOfYear == 187);
   6684     }
   6685 
   6686     /++
   6687         Day of the year.
   6688 
   6689         Params:
   6690             day = The day of the year to set which day of the year this
   6691                   $(LREF Date) is on.
   6692 
   6693         Throws:
   6694             $(REF DateTimeException,std,datetime,date) if the given day is an
   6695             invalid day of the year.
   6696       +/
   6697     @property void dayOfYear(int day) @safe pure
   6698     {
   6699         setDayOfYear!true(day);
   6700     }
   6701 
   6702     private void setDayOfYear(bool useExceptions = false)(int day)
   6703     {
   6704         immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
   6705 
   6706         bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
   6707         enum errorMsg = "Invalid day of the year.";
   6708 
   6709         static if (useExceptions)
   6710         {
   6711             if (dayOutOfRange) throw new DateTimeException(errorMsg);
   6712         }
   6713         else
   6714         {
   6715             assert(!dayOutOfRange, errorMsg);
   6716         }
   6717 
   6718         foreach (i; 1 .. lastDay.length)
   6719         {
   6720             if (day <= lastDay[i])
   6721             {
   6722                 _month = cast(Month)(cast(int) Month.jan + i - 1);
   6723                 _day = cast(ubyte)(day - lastDay[i - 1]);
   6724                 return;
   6725             }
   6726         }
   6727         assert(0, "Invalid day of the year.");
   6728     }
   6729 
   6730     @safe unittest
   6731     {
   6732         static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
   6733         {
   6734             date.dayOfYear = day;
   6735             assert(date.month == expected.month);
   6736             assert(date.day == expected.day);
   6737         }
   6738 
   6739         foreach (doy; testDaysOfYear)
   6740         {
   6741             test(Date(1999, 1, 1), doy.day, doy.md);
   6742             test(Date(-1, 1, 1), doy.day, doy.md);
   6743         }
   6744 
   6745         foreach (doy; testDaysOfLeapYear)
   6746         {
   6747             test(Date(2000, 1, 1), doy.day, doy.md);
   6748             test(Date(-4, 1, 1), doy.day, doy.md);
   6749         }
   6750 
   6751         const cdate = Date(1999, 7, 6);
   6752         immutable idate = Date(1999, 7, 6);
   6753         static assert(!__traits(compiles, cdate.dayOfYear = 187));
   6754         static assert(!__traits(compiles, idate.dayOfYear = 187));
   6755     }
   6756 
   6757 
   6758     /++
   6759         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
   6760      +/
   6761     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
   6762     {
   6763         if (isAD)
   6764         {
   6765             if (_year == 1)
   6766                 return dayOfYear;
   6767 
   6768             int years = _year - 1;
   6769             auto days = (years / 400) * daysIn400Years;
   6770             years %= 400;
   6771 
   6772             days += (years / 100) * daysIn100Years;
   6773             years %= 100;
   6774 
   6775             days += (years / 4) * daysIn4Years;
   6776             years %= 4;
   6777 
   6778             days += years * daysInYear;
   6779 
   6780             days += dayOfYear;
   6781 
   6782             return days;
   6783         }
   6784         else if (_year == 0)
   6785             return dayOfYear - daysInLeapYear;
   6786         else
   6787         {
   6788             int years = _year;
   6789             auto days = (years / 400) * daysIn400Years;
   6790             years %= 400;
   6791 
   6792             days += (years / 100) * daysIn100Years;
   6793             years %= 100;
   6794 
   6795             days += (years / 4) * daysIn4Years;
   6796             years %= 4;
   6797 
   6798             if (years < 0)
   6799             {
   6800                 days -= daysInLeapYear;
   6801                 ++years;
   6802 
   6803                 days += years * daysInYear;
   6804 
   6805                 days -= daysInYear - dayOfYear;
   6806             }
   6807             else
   6808                 days -= daysInLeapYear - dayOfYear;
   6809 
   6810             return days;
   6811         }
   6812     }
   6813 
   6814     ///
   6815     @safe unittest
   6816     {
   6817         assert(Date(1, 1, 1).dayOfGregorianCal == 1);
   6818         assert(Date(1, 12, 31).dayOfGregorianCal == 365);
   6819         assert(Date(2, 1, 1).dayOfGregorianCal == 366);
   6820 
   6821         assert(Date(0, 12, 31).dayOfGregorianCal == 0);
   6822         assert(Date(0, 1, 1).dayOfGregorianCal == -365);
   6823         assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
   6824 
   6825         assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
   6826         assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
   6827     }
   6828 
   6829     @safe unittest
   6830     {
   6831         import std.range : chain;
   6832 
   6833         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
   6834             assert(gd.date.dayOfGregorianCal == gd.day);
   6835 
   6836         auto date = Date(1999, 7, 6);
   6837         const cdate = Date(1999, 7, 6);
   6838         immutable idate = Date(1999, 7, 6);
   6839         assert(date.dayOfGregorianCal == 729_941);
   6840         assert(cdate.dayOfGregorianCal == 729_941);
   6841         assert(idate.dayOfGregorianCal == 729_941);
   6842     }
   6843 
   6844     /++
   6845         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
   6846 
   6847         Params:
   6848             day = The day of the Gregorian Calendar to set this $(LREF Date) to.
   6849      +/
   6850     @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
   6851     {
   6852         this = Date(day);
   6853     }
   6854 
   6855     ///
   6856     @safe unittest
   6857     {
   6858         auto date = Date.init;
   6859         date.dayOfGregorianCal = 1;
   6860         assert(date == Date(1, 1, 1));
   6861 
   6862         date.dayOfGregorianCal = 365;
   6863         assert(date == Date(1, 12, 31));
   6864 
   6865         date.dayOfGregorianCal = 366;
   6866         assert(date == Date(2, 1, 1));
   6867 
   6868         date.dayOfGregorianCal = 0;
   6869         assert(date == Date(0, 12, 31));
   6870 
   6871         date.dayOfGregorianCal = -365;
   6872         assert(date == Date(-0, 1, 1));
   6873 
   6874         date.dayOfGregorianCal = -366;
   6875         assert(date == Date(-1, 12, 31));
   6876 
   6877         date.dayOfGregorianCal = 730_120;
   6878         assert(date == Date(2000, 1, 1));
   6879 
   6880         date.dayOfGregorianCal = 734_137;
   6881         assert(date == Date(2010, 12, 31));
   6882     }
   6883 
   6884     @safe unittest
   6885     {
   6886         auto date = Date(1999, 7, 6);
   6887         const cdate = Date(1999, 7, 6);
   6888         immutable idate = Date(1999, 7, 6);
   6889         date.dayOfGregorianCal = 187;
   6890         assert(date.dayOfGregorianCal == 187);
   6891         static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
   6892         static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
   6893     }
   6894 
   6895 
   6896     /++
   6897         The ISO 8601 week and year of the year that this $(LREF Date) is in.
   6898 
   6899         Returns:
   6900             An anonymous struct with the members $(D isoWeekYear) for the
   6901             resulting year and $(D isoWeek) for the resulting ISO week.
   6902 
   6903         See_Also:
   6904             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
   6905       +/
   6906     @property auto isoWeekAndYear() const @safe pure nothrow
   6907     {
   6908         struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
   6909 
   6910         immutable weekday = dayOfWeek;
   6911         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
   6912         immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
   6913 
   6914         try
   6915         {
   6916             if (week == 53)
   6917             {
   6918                 switch (Date(_year + 1, 1, 1).dayOfWeek)
   6919                 {
   6920                     case DayOfWeek.mon:
   6921                     case DayOfWeek.tue:
   6922                     case DayOfWeek.wed:
   6923                     case DayOfWeek.thu:
   6924                         return ISOWeekAndYear(cast(short) (_year + 1), 1);
   6925                     case DayOfWeek.fri:
   6926                     case DayOfWeek.sat:
   6927                     case DayOfWeek.sun:
   6928                         return ISOWeekAndYear(_year, 53);
   6929                     default:
   6930                         assert(0, "Invalid ISO Week");
   6931                 }
   6932             }
   6933             else if (week > 0)
   6934                 return ISOWeekAndYear(_year, cast(ubyte) week);
   6935             else
   6936                 return Date(_year - 1, 12, 31).isoWeekAndYear;
   6937         }
   6938         catch (Exception e)
   6939             assert(0, "Date's constructor threw.");
   6940     }
   6941 
   6942     /++
   6943         The ISO 8601 week of the year that this $(LREF Date) is in.
   6944 
   6945         See_Also:
   6946             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
   6947       +/
   6948     @property ubyte isoWeek() const @safe pure nothrow
   6949     {
   6950         return isoWeekAndYear().isoWeek;
   6951     }
   6952 
   6953     @safe unittest
   6954     {
   6955         // Test A.D.
   6956         assert(Date(2009, 12, 28).isoWeek == 53);
   6957         assert(Date(2009, 12, 29).isoWeek == 53);
   6958         assert(Date(2009, 12, 30).isoWeek == 53);
   6959         assert(Date(2009, 12, 31).isoWeek == 53);
   6960         assert(Date(2010, 1, 1).isoWeek == 53);
   6961         assert(Date(2010, 1, 2).isoWeek == 53);
   6962         assert(Date(2010, 1, 3).isoWeek == 53);
   6963         assert(Date(2010, 1, 4).isoWeek == 1);
   6964         assert(Date(2010, 1, 5).isoWeek == 1);
   6965         assert(Date(2010, 1, 6).isoWeek == 1);
   6966         assert(Date(2010, 1, 7).isoWeek == 1);
   6967         assert(Date(2010, 1, 8).isoWeek == 1);
   6968         assert(Date(2010, 1, 9).isoWeek == 1);
   6969         assert(Date(2010, 1, 10).isoWeek == 1);
   6970         assert(Date(2010, 1, 11).isoWeek == 2);
   6971         assert(Date(2010, 12, 31).isoWeek == 52);
   6972 
   6973         assert(Date(2004, 12, 26).isoWeek == 52);
   6974         assert(Date(2004, 12, 27).isoWeek == 53);
   6975         assert(Date(2004, 12, 28).isoWeek == 53);
   6976         assert(Date(2004, 12, 29).isoWeek == 53);
   6977         assert(Date(2004, 12, 30).isoWeek == 53);
   6978         assert(Date(2004, 12, 31).isoWeek == 53);
   6979         assert(Date(2005, 1, 1).isoWeek == 53);
   6980         assert(Date(2005, 1, 2).isoWeek == 53);
   6981 
   6982         assert(Date(2005, 12, 31).isoWeek == 52);
   6983         assert(Date(2007, 1, 1).isoWeek == 1);
   6984 
   6985         assert(Date(2007, 12, 30).isoWeek == 52);
   6986         assert(Date(2007, 12, 31).isoWeek == 1);
   6987         assert(Date(2008, 1, 1).isoWeek == 1);
   6988 
   6989         assert(Date(2008, 12, 28).isoWeek == 52);
   6990         assert(Date(2008, 12, 29).isoWeek == 1);
   6991         assert(Date(2008, 12, 30).isoWeek == 1);
   6992         assert(Date(2008, 12, 31).isoWeek == 1);
   6993         assert(Date(2009, 1, 1).isoWeek == 1);
   6994         assert(Date(2009, 1, 2).isoWeek == 1);
   6995         assert(Date(2009, 1, 3).isoWeek == 1);
   6996         assert(Date(2009, 1, 4).isoWeek == 1);
   6997 
   6998         // Test B.C.
   6999         // The algorithm should work identically for both A.D. and B.C. since
   7000         // it doesn't really take the year into account, so B.C. testing
   7001         // probably isn't really needed.
   7002         assert(Date(0, 12, 31).isoWeek == 52);
   7003         assert(Date(0, 1, 4).isoWeek == 1);
   7004         assert(Date(0, 1, 1).isoWeek == 52);
   7005 
   7006         const cdate = Date(1999, 7, 6);
   7007         immutable idate = Date(1999, 7, 6);
   7008         assert(cdate.isoWeek == 27);
   7009         static assert(!__traits(compiles, cdate.isoWeek = 3));
   7010         assert(idate.isoWeek == 27);
   7011         static assert(!__traits(compiles, idate.isoWeek = 3));
   7012     }
   7013 
   7014     /++
   7015         The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
   7016 
   7017         May differ from $(LREF year) between 28 December and 4 January.
   7018 
   7019         See_Also:
   7020             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
   7021       +/
   7022     @property short isoWeekYear() const @safe pure nothrow
   7023     {
   7024         return isoWeekAndYear().isoWeekYear;
   7025     }
   7026 
   7027     @safe unittest
   7028     {
   7029         // Test A.D.
   7030         assert(Date(2009, 12, 28).isoWeekYear == 2009);
   7031         assert(Date(2009, 12, 29).isoWeekYear == 2009);
   7032         assert(Date(2009, 12, 30).isoWeekYear == 2009);
   7033         assert(Date(2009, 12, 31).isoWeekYear == 2009);
   7034         assert(Date(2010, 1, 1).isoWeekYear == 2009);
   7035         assert(Date(2010, 1, 2).isoWeekYear == 2009);
   7036         assert(Date(2010, 1, 3).isoWeekYear == 2009);
   7037         assert(Date(2010, 1, 4).isoWeekYear == 2010);
   7038         assert(Date(2010, 1, 5).isoWeekYear == 2010);
   7039         assert(Date(2010, 1, 6).isoWeekYear == 2010);
   7040         assert(Date(2010, 1, 7).isoWeekYear == 2010);
   7041         assert(Date(2010, 1, 8).isoWeekYear == 2010);
   7042         assert(Date(2010, 1, 9).isoWeekYear == 2010);
   7043         assert(Date(2010, 1, 10).isoWeekYear == 2010);
   7044         assert(Date(2010, 1, 11).isoWeekYear == 2010);
   7045         assert(Date(2010, 12, 31).isoWeekYear == 2010);
   7046 
   7047         assert(Date(2004, 12, 26).isoWeekYear == 2004);
   7048         assert(Date(2004, 12, 27).isoWeekYear == 2004);
   7049         assert(Date(2004, 12, 28).isoWeekYear == 2004);
   7050         assert(Date(2004, 12, 29).isoWeekYear == 2004);
   7051         assert(Date(2004, 12, 30).isoWeekYear == 2004);
   7052         assert(Date(2004, 12, 31).isoWeekYear == 2004);
   7053         assert(Date(2005, 1, 1).isoWeekYear == 2004);
   7054         assert(Date(2005, 1, 2).isoWeekYear == 2004);
   7055         assert(Date(2005, 1, 3).isoWeekYear == 2005);
   7056 
   7057         assert(Date(2005, 12, 31).isoWeekYear == 2005);
   7058         assert(Date(2007, 1, 1).isoWeekYear == 2007);
   7059 
   7060         assert(Date(2007, 12, 30).isoWeekYear == 2007);
   7061         assert(Date(2007, 12, 31).isoWeekYear == 2008);
   7062         assert(Date(2008, 1, 1).isoWeekYear == 2008);
   7063 
   7064         assert(Date(2008, 12, 28).isoWeekYear == 2008);
   7065         assert(Date(2008, 12, 29).isoWeekYear == 2009);
   7066         assert(Date(2008, 12, 30).isoWeekYear == 2009);
   7067         assert(Date(2008, 12, 31).isoWeekYear == 2009);
   7068         assert(Date(2009, 1, 1).isoWeekYear == 2009);
   7069         assert(Date(2009, 1, 2).isoWeekYear == 2009);
   7070         assert(Date(2009, 1, 3).isoWeekYear == 2009);
   7071         assert(Date(2009, 1, 4).isoWeekYear == 2009);
   7072 
   7073         // Test B.C.
   7074         assert(Date(0, 12, 31).isoWeekYear == 0);
   7075         assert(Date(0, 1, 4).isoWeekYear == 0);
   7076         assert(Date(0, 1, 1).isoWeekYear == -1);
   7077 
   7078         const cdate = Date(1999, 7, 6);
   7079         immutable idate = Date(1999, 7, 6);
   7080         assert(cdate.isoWeekYear == 1999);
   7081         assert(idate.isoWeekYear == 1999);
   7082     }
   7083 
   7084     static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
   7085     {
   7086         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
   7087         immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
   7088 
   7089         Date date;
   7090         date._year = isoWeekYear;
   7091         date._month = Month.jan;
   7092         date._day = 3;
   7093         immutable startOfYear = date.dayOfWeek;
   7094         return date._addDays(dayOffset - startOfYear);
   7095     }
   7096 
   7097     @safe unittest
   7098     {
   7099         // Test -30000 days to 30000 days for matching construction <-> deconstruction
   7100         Date date = Date(1, 1, 1);
   7101         date._addDays(-30_000);
   7102         foreach (day; 0 .. 60_000)
   7103         {
   7104             const year = date.isoWeekYear;
   7105             const dow = date.dayOfWeek;
   7106             const isoWeek = date.isoWeek;
   7107             const reversed = Date.fromISOWeek(year, isoWeek, dow);
   7108             assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
   7109             date = date._addDays(1);
   7110         }
   7111     }
   7112 
   7113 
   7114     /++
   7115         $(LREF Date) for the last day in the month that this $(LREF Date) is in.
   7116       +/
   7117     @property Date endOfMonth() const @safe pure nothrow
   7118     {
   7119         try
   7120             return Date(_year, _month, maxDay(_year, _month));
   7121         catch (Exception e)
   7122             assert(0, "Date's constructor threw.");
   7123     }
   7124 
   7125     ///
   7126     @safe unittest
   7127     {
   7128         assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
   7129         assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
   7130         assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
   7131         assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
   7132     }
   7133 
   7134     @safe unittest
   7135     {
   7136         // Test A.D.
   7137         assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
   7138         assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
   7139         assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
   7140         assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
   7141         assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
   7142         assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
   7143         assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
   7144         assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
   7145         assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
   7146         assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
   7147         assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
   7148         assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
   7149         assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
   7150 
   7151         // Test B.C.
   7152         assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
   7153         assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
   7154         assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
   7155         assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
   7156         assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
   7157         assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
   7158         assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
   7159         assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
   7160         assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
   7161         assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
   7162         assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
   7163         assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
   7164         assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
   7165 
   7166         const cdate = Date(1999, 7, 6);
   7167         immutable idate = Date(1999, 7, 6);
   7168         static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
   7169         static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
   7170     }
   7171 
   7172 
   7173     /++
   7174         The last day in the month that this $(LREF Date) is in.
   7175       +/
   7176     @property ubyte daysInMonth() const @safe pure nothrow @nogc
   7177     {
   7178         return maxDay(_year, _month);
   7179     }
   7180 
   7181     ///
   7182     @safe unittest
   7183     {
   7184         assert(Date(1999, 1, 6).daysInMonth == 31);
   7185         assert(Date(1999, 2, 7).daysInMonth == 28);
   7186         assert(Date(2000, 2, 7).daysInMonth == 29);
   7187         assert(Date(2000, 6, 4).daysInMonth == 30);
   7188     }
   7189 
   7190     @safe unittest
   7191     {
   7192         // Test A.D.
   7193         assert(Date(1999, 1, 1).daysInMonth == 31);
   7194         assert(Date(1999, 2, 1).daysInMonth == 28);
   7195         assert(Date(2000, 2, 1).daysInMonth == 29);
   7196         assert(Date(1999, 3, 1).daysInMonth == 31);
   7197         assert(Date(1999, 4, 1).daysInMonth == 30);
   7198         assert(Date(1999, 5, 1).daysInMonth == 31);
   7199         assert(Date(1999, 6, 1).daysInMonth == 30);
   7200         assert(Date(1999, 7, 1).daysInMonth == 31);
   7201         assert(Date(1999, 8, 1).daysInMonth == 31);
   7202         assert(Date(1999, 9, 1).daysInMonth == 30);
   7203         assert(Date(1999, 10, 1).daysInMonth == 31);
   7204         assert(Date(1999, 11, 1).daysInMonth == 30);
   7205         assert(Date(1999, 12, 1).daysInMonth == 31);
   7206 
   7207         // Test B.C.
   7208         assert(Date(-1999, 1, 1).daysInMonth == 31);
   7209         assert(Date(-1999, 2, 1).daysInMonth == 28);
   7210         assert(Date(-2000, 2, 1).daysInMonth == 29);
   7211         assert(Date(-1999, 3, 1).daysInMonth == 31);
   7212         assert(Date(-1999, 4, 1).daysInMonth == 30);
   7213         assert(Date(-1999, 5, 1).daysInMonth == 31);
   7214         assert(Date(-1999, 6, 1).daysInMonth == 30);
   7215         assert(Date(-1999, 7, 1).daysInMonth == 31);
   7216         assert(Date(-1999, 8, 1).daysInMonth == 31);
   7217         assert(Date(-1999, 9, 1).daysInMonth == 30);
   7218         assert(Date(-1999, 10, 1).daysInMonth == 31);
   7219         assert(Date(-1999, 11, 1).daysInMonth == 30);
   7220         assert(Date(-1999, 12, 1).daysInMonth == 31);
   7221 
   7222         const cdate = Date(1999, 7, 6);
   7223         immutable idate = Date(1999, 7, 6);
   7224         static assert(!__traits(compiles, cdate.daysInMonth = 30));
   7225         static assert(!__traits(compiles, idate.daysInMonth = 30));
   7226     }
   7227 
   7228 
   7229     /++
   7230         Whether the current year is a date in A.D.
   7231       +/
   7232     @property bool isAD() const @safe pure nothrow @nogc
   7233     {
   7234         return _year > 0;
   7235     }
   7236 
   7237     ///
   7238     @safe unittest
   7239     {
   7240         assert(Date(1, 1, 1).isAD);
   7241         assert(Date(2010, 12, 31).isAD);
   7242         assert(!Date(0, 12, 31).isAD);
   7243         assert(!Date(-2010, 1, 1).isAD);
   7244     }
   7245 
   7246     @safe unittest
   7247     {
   7248         assert(Date(2010, 7, 4).isAD);
   7249         assert(Date(1, 1, 1).isAD);
   7250         assert(!Date(0, 1, 1).isAD);
   7251         assert(!Date(-1, 1, 1).isAD);
   7252         assert(!Date(-2010, 7, 4).isAD);
   7253 
   7254         const cdate = Date(1999, 7, 6);
   7255         immutable idate = Date(1999, 7, 6);
   7256         assert(cdate.isAD);
   7257         assert(idate.isAD);
   7258     }
   7259 
   7260 
   7261     /++
   7262         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
   7263         $(LREF Date) at noon (since the Julian day changes at noon).
   7264       +/
   7265     @property long julianDay() const @safe pure nothrow @nogc
   7266     {
   7267         return dayOfGregorianCal + 1_721_425;
   7268     }
   7269 
   7270     @safe unittest
   7271     {
   7272         assert(Date(-4713, 11, 24).julianDay == 0);
   7273         assert(Date(0, 12, 31).julianDay == 1_721_425);
   7274         assert(Date(1, 1, 1).julianDay == 1_721_426);
   7275         assert(Date(1582, 10, 15).julianDay == 2_299_161);
   7276         assert(Date(1858, 11, 17).julianDay == 2_400_001);
   7277         assert(Date(1982, 1, 4).julianDay == 2_444_974);
   7278         assert(Date(1996, 3, 31).julianDay == 2_450_174);
   7279         assert(Date(2010, 8, 24).julianDay == 2_455_433);
   7280 
   7281         const cdate = Date(1999, 7, 6);
   7282         immutable idate = Date(1999, 7, 6);
   7283         assert(cdate.julianDay == 2_451_366);
   7284         assert(idate.julianDay == 2_451_366);
   7285     }
   7286 
   7287 
   7288     /++
   7289         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
   7290         any time on this date (since, the modified Julian day changes at
   7291         midnight).
   7292       +/
   7293     @property long modJulianDay() const @safe pure nothrow @nogc
   7294     {
   7295         return julianDay - 2_400_001;
   7296     }
   7297 
   7298     @safe unittest
   7299     {
   7300         assert(Date(1858, 11, 17).modJulianDay == 0);
   7301         assert(Date(2010, 8, 24).modJulianDay == 55_432);
   7302 
   7303         const cdate = Date(1999, 7, 6);
   7304         immutable idate = Date(1999, 7, 6);
   7305         assert(cdate.modJulianDay == 51_365);
   7306         assert(idate.modJulianDay == 51_365);
   7307     }
   7308 
   7309 
   7310     /++
   7311         Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
   7312         If `writer` is set, the resulting string will be written directly
   7313         to it.
   7314 
   7315         Params:
   7316             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   7317         Returns:
   7318             A `string` when not using an output range; `void` otherwise.
   7319       +/
   7320     string toISOString() const @safe pure nothrow
   7321     {
   7322         import std.array : appender;
   7323         auto w = appender!string();
   7324         w.reserve(8);
   7325         try
   7326             toISOString(w);
   7327         catch (Exception e)
   7328             assert(0, "toISOString() threw.");
   7329         return w.data;
   7330     }
   7331 
   7332     ///
   7333     @safe unittest
   7334     {
   7335         assert(Date(2010, 7, 4).toISOString() == "20100704");
   7336         assert(Date(1998, 12, 25).toISOString() == "19981225");
   7337         assert(Date(0, 1, 5).toISOString() == "00000105");
   7338         assert(Date(-4, 1, 5).toISOString() == "-00040105");
   7339     }
   7340 
   7341     @safe unittest
   7342     {
   7343         // Test A.D.
   7344         assert(Date(9, 12, 4).toISOString() == "00091204");
   7345         assert(Date(99, 12, 4).toISOString() == "00991204");
   7346         assert(Date(999, 12, 4).toISOString() == "09991204");
   7347         assert(Date(9999, 7, 4).toISOString() == "99990704");
   7348         assert(Date(10000, 10, 20).toISOString() == "+100001020");
   7349 
   7350         // Test B.C.
   7351         assert(Date(0, 12, 4).toISOString() == "00001204");
   7352         assert(Date(-9, 12, 4).toISOString() == "-00091204");
   7353         assert(Date(-99, 12, 4).toISOString() == "-00991204");
   7354         assert(Date(-999, 12, 4).toISOString() == "-09991204");
   7355         assert(Date(-9999, 7, 4).toISOString() == "-99990704");
   7356         assert(Date(-10000, 10, 20).toISOString() == "-100001020");
   7357 
   7358         const cdate = Date(1999, 7, 6);
   7359         immutable idate = Date(1999, 7, 6);
   7360         assert(cdate.toISOString() == "19990706");
   7361         assert(idate.toISOString() == "19990706");
   7362     }
   7363 
   7364     /// ditto
   7365     void toISOString(W)(ref W writer) const
   7366     if (isOutputRange!(W, char))
   7367     {
   7368         import std.format.write : formattedWrite;
   7369         if (_year >= 0)
   7370         {
   7371             if (_year < 10_000)
   7372                 formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
   7373             else
   7374                 formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
   7375         }
   7376         else if (_year > -10_000)
   7377             formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
   7378         else
   7379             formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
   7380     }
   7381 
   7382     @safe pure unittest
   7383     {
   7384         import std.array : appender;
   7385 
   7386         auto w = appender!(char[])();
   7387         Date(2010, 7, 4).toISOString(w);
   7388         assert(w.data == "20100704");
   7389         w.clear();
   7390         Date(1998, 12, 25).toISOString(w);
   7391         assert(w.data == "19981225");
   7392     }
   7393 
   7394     /++
   7395         Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
   7396         If `writer` is set, the resulting string will be written directly
   7397         to it.
   7398 
   7399         Params:
   7400             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   7401         Returns:
   7402             A `string` when not using an output range; `void` otherwise.
   7403       +/
   7404     string toISOExtString() const @safe pure nothrow
   7405     {
   7406         import std.array : appender;
   7407         auto w = appender!string();
   7408         w.reserve(10);
   7409         try
   7410             toISOExtString(w);
   7411         catch (Exception e)
   7412             assert(0, "toISOExtString() threw.");
   7413         return w.data;
   7414     }
   7415 
   7416     ///
   7417     @safe unittest
   7418     {
   7419         assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
   7420         assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
   7421         assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
   7422         assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
   7423     }
   7424 
   7425     @safe unittest
   7426     {
   7427         // Test A.D.
   7428         assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
   7429         assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
   7430         assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
   7431         assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
   7432         assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
   7433 
   7434         // Test B.C.
   7435         assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
   7436         assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
   7437         assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
   7438         assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
   7439         assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
   7440         assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
   7441 
   7442         const cdate = Date(1999, 7, 6);
   7443         immutable idate = Date(1999, 7, 6);
   7444         assert(cdate.toISOExtString() == "1999-07-06");
   7445         assert(idate.toISOExtString() == "1999-07-06");
   7446     }
   7447 
   7448     /// ditto
   7449     void toISOExtString(W)(ref W writer) const
   7450     if (isOutputRange!(W, char))
   7451     {
   7452         import std.format.write : formattedWrite;
   7453         if (_year >= 0)
   7454         {
   7455             if (_year < 10_000)
   7456                 formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
   7457             else
   7458                 formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
   7459         }
   7460         else if (_year > -10_000)
   7461             formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
   7462         else
   7463             formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
   7464     }
   7465 
   7466     @safe pure unittest
   7467     {
   7468         import std.array : appender;
   7469 
   7470         auto w = appender!(char[])();
   7471         Date(2010, 7, 4).toISOExtString(w);
   7472         assert(w.data == "2010-07-04");
   7473         w.clear();
   7474         Date(-4, 1, 5).toISOExtString(w);
   7475         assert(w.data == "-0004-01-05");
   7476     }
   7477 
   7478     /++
   7479         Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
   7480         If `writer` is set, the resulting string will be written directly
   7481         to it.
   7482 
   7483         Params:
   7484             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   7485         Returns:
   7486             A `string` when not using an output range; `void` otherwise.
   7487       +/
   7488     string toSimpleString() const @safe pure nothrow
   7489     {
   7490         import std.array : appender;
   7491         auto w = appender!string();
   7492         w.reserve(11);
   7493         try
   7494             toSimpleString(w);
   7495         catch (Exception e)
   7496             assert(0, "toSimpleString() threw.");
   7497         return w.data;
   7498     }
   7499 
   7500     ///
   7501     @safe unittest
   7502     {
   7503         assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
   7504         assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
   7505         assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
   7506         assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
   7507     }
   7508 
   7509     @safe unittest
   7510     {
   7511         // Test A.D.
   7512         assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
   7513         assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
   7514         assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
   7515         assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
   7516         assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
   7517 
   7518         // Test B.C.
   7519         assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
   7520         assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
   7521         assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
   7522         assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
   7523         assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
   7524         assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
   7525 
   7526         const cdate = Date(1999, 7, 6);
   7527         immutable idate = Date(1999, 7, 6);
   7528         assert(cdate.toSimpleString() == "1999-Jul-06");
   7529         assert(idate.toSimpleString() == "1999-Jul-06");
   7530     }
   7531 
   7532     /// ditto
   7533     void toSimpleString(W)(ref W writer) const
   7534     if (isOutputRange!(W, char))
   7535     {
   7536         import std.format.write : formattedWrite;
   7537         if (_year >= 0)
   7538         {
   7539             if (_year < 10_000)
   7540                 formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
   7541             else
   7542                 formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
   7543         }
   7544         else if (_year > -10_000)
   7545             formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
   7546         else
   7547             formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
   7548     }
   7549 
   7550     @safe pure unittest
   7551     {
   7552         import std.array : appender;
   7553 
   7554         auto w = appender!(char[])();
   7555         Date(9, 12, 4).toSimpleString(w);
   7556         assert(w.data == "0009-Dec-04");
   7557         w.clear();
   7558         Date(-10000, 10, 20).toSimpleString(w);
   7559         assert(w.data == "-10000-Oct-20");
   7560     }
   7561 
   7562     /++
   7563         Converts this $(LREF Date) to a string.
   7564 
   7565         This function exists to make it easy to convert a $(LREF Date) to a
   7566         string for code that does not care what the exact format is - just that
   7567         it presents the information in a clear manner. It also makes it easy to
   7568         simply convert a $(LREF Date) to a string when using functions such as
   7569         `to!string`, `format`, or `writeln` which use toString to convert
   7570         user-defined types. So, it is unlikely that much code will call
   7571         toString directly.
   7572 
   7573         The format of the string is purposefully unspecified, and code that
   7574         cares about the format of the string should use `toISOString`,
   7575         `toISOExtString`, `toSimpleString`, or some other custom formatting
   7576         function that explicitly generates the format that the code needs. The
   7577         reason is that the code is then clear about what format it's using,
   7578         making it less error-prone to maintain the code and interact with other
   7579         software that consumes the generated strings. It's for this same reason
   7580         $(LREF Date) has no `fromString` function, whereas it does have
   7581         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
   7582 
   7583         The format returned by toString may or may not change in the future.
   7584       +/
   7585     string toString() const @safe pure nothrow
   7586     {
   7587         return toSimpleString();
   7588     }
   7589 
   7590     @safe unittest
   7591     {
   7592         auto date = Date(1999, 7, 6);
   7593         const cdate = Date(1999, 7, 6);
   7594         immutable idate = Date(1999, 7, 6);
   7595         assert(date.toString());
   7596         assert(cdate.toString());
   7597         assert(idate.toString());
   7598     }
   7599 
   7600     /// ditto
   7601     void toString(W)(ref W writer) const
   7602     if (isOutputRange!(W, char))
   7603     {
   7604         toSimpleString(writer);
   7605     }
   7606 
   7607     /++
   7608         Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
   7609         is stripped from the given string.
   7610 
   7611         Params:
   7612             isoString = A string formatted in the ISO format for dates.
   7613 
   7614         Throws:
   7615             $(REF DateTimeException,std,datetime,date) if the given string is
   7616             not in the ISO format or if the resulting $(LREF Date) would not be
   7617             valid.
   7618       +/
   7619     static Date fromISOString(S)(scope const S isoString) @safe pure
   7620         if (isSomeString!S)
   7621     {
   7622         import std.algorithm.searching : startsWith;
   7623         import std.conv : to, text, ConvException;
   7624         import std.exception : enforce;
   7625         import std.string : strip;
   7626 
   7627         auto str = isoString.strip;
   7628 
   7629         enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString));
   7630 
   7631         int day, month, year;
   7632         auto yearStr = str[0 .. $ - 4];
   7633 
   7634         try
   7635         {
   7636             // using conversion to uint plus cast because it checks for +/-
   7637             // for us quickly while throwing ConvException
   7638             day = cast(int) to!uint(str[$ - 2 .. $]);
   7639             month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
   7640 
   7641             if (yearStr.length > 4)
   7642             {
   7643                 enforce!DateTimeException(yearStr.startsWith('-', '+'),
   7644                         text("Invalid ISO String: ", isoString));
   7645                 year = to!int(yearStr);
   7646             }
   7647             else
   7648             {
   7649                 year = cast(int) to!uint(yearStr);
   7650             }
   7651         }
   7652         catch (ConvException)
   7653         {
   7654             throw new DateTimeException(text("Invalid ISO String: ", isoString));
   7655         }
   7656 
   7657         return Date(year, month, day);
   7658     }
   7659 
   7660     ///
   7661     @safe unittest
   7662     {
   7663         assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
   7664         assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
   7665         assert(Date.fromISOString("00000105") == Date(0, 1, 5));
   7666         assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
   7667         assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
   7668     }
   7669 
   7670     @safe unittest
   7671     {
   7672         assertThrown!DateTimeException(Date.fromISOString(""));
   7673         assertThrown!DateTimeException(Date.fromISOString("990704"));
   7674         assertThrown!DateTimeException(Date.fromISOString("0100704"));
   7675         assertThrown!DateTimeException(Date.fromISOString("2010070"));
   7676         assertThrown!DateTimeException(Date.fromISOString("2010070 "));
   7677         assertThrown!DateTimeException(Date.fromISOString("120100704"));
   7678         assertThrown!DateTimeException(Date.fromISOString("-0100704"));
   7679         assertThrown!DateTimeException(Date.fromISOString("+0100704"));
   7680         assertThrown!DateTimeException(Date.fromISOString("2010070a"));
   7681         assertThrown!DateTimeException(Date.fromISOString("20100a04"));
   7682         assertThrown!DateTimeException(Date.fromISOString("2010a704"));
   7683 
   7684         assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
   7685         assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
   7686         assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
   7687         assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
   7688         assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
   7689         assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
   7690         assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
   7691         assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
   7692         assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
   7693         assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
   7694         assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
   7695         assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
   7696         assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
   7697         assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
   7698         assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
   7699         assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
   7700         assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
   7701 
   7702         assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
   7703         assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
   7704         assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
   7705         assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
   7706         assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
   7707         assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
   7708         assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
   7709         assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
   7710         assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
   7711         assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
   7712 
   7713         assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
   7714         assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
   7715         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
   7716         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
   7717         assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
   7718         assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
   7719         assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
   7720         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
   7721         assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
   7722         assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
   7723         assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
   7724 
   7725         assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
   7726         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
   7727 
   7728         assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
   7729         assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
   7730         assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
   7731         assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
   7732         assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
   7733         assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
   7734     }
   7735 
   7736     // https://issues.dlang.org/show_bug.cgi?id=17801
   7737     @safe unittest
   7738     {
   7739         import std.conv : to;
   7740         import std.meta : AliasSeq;
   7741         static foreach (C; AliasSeq!(char, wchar, dchar))
   7742         {
   7743             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   7744                 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
   7745         }
   7746     }
   7747 
   7748 
   7749     /++
   7750         Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
   7751         Whitespace is stripped from the given string.
   7752 
   7753         Params:
   7754             isoExtString = A string formatted in the ISO Extended format for
   7755                            dates.
   7756 
   7757         Throws:
   7758             $(REF DateTimeException,std,datetime,date) if the given string is
   7759             not in the ISO Extended format or if the resulting $(LREF Date)
   7760             would not be valid.
   7761       +/
   7762     static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
   7763         if (isSomeString!(S))
   7764     {
   7765         import std.algorithm.searching : startsWith;
   7766         import std.conv : to, ConvException;
   7767         import std.format : format;
   7768         import std.string : strip;
   7769 
   7770         auto str = strip(isoExtString);
   7771         short year;
   7772         ubyte month, day;
   7773 
   7774         if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
   7775             throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
   7776 
   7777         auto yearStr = str[0 .. $-6];
   7778         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
   7779         if ((yearStr.length > 4) != signAtBegining)
   7780         {
   7781             throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
   7782         }
   7783 
   7784         try
   7785         {
   7786             day = to!ubyte(str[$-2 .. $]);
   7787             month = to!ubyte(str[$-5 .. $-3]);
   7788             year = to!short(yearStr);
   7789         }
   7790         catch (ConvException)
   7791         {
   7792             throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
   7793         }
   7794 
   7795         return Date(year, month, day);
   7796     }
   7797 
   7798     ///
   7799     @safe unittest
   7800     {
   7801         assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
   7802         assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
   7803         assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
   7804         assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
   7805         assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
   7806     }
   7807 
   7808     @safe unittest
   7809     {
   7810         assertThrown!DateTimeException(Date.fromISOExtString(""));
   7811         assertThrown!DateTimeException(Date.fromISOExtString("990704"));
   7812         assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
   7813         assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
   7814         assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
   7815         assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
   7816         assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
   7817         assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
   7818         assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
   7819         assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
   7820         assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
   7821 
   7822         assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
   7823         assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
   7824         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
   7825         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
   7826         assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
   7827         assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
   7828         assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
   7829         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
   7830         assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
   7831         assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
   7832         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
   7833         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
   7834         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
   7835         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
   7836         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
   7837         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
   7838         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
   7839 
   7840         assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
   7841         assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
   7842         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
   7843         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
   7844         assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
   7845         assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
   7846         assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
   7847         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
   7848         assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
   7849         assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
   7850 
   7851         assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
   7852         assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
   7853         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
   7854         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
   7855         assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
   7856         assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
   7857         assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
   7858         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
   7859         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
   7860         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
   7861         assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
   7862 
   7863         assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
   7864         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
   7865 
   7866         assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
   7867         assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
   7868         assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
   7869         assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
   7870         assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
   7871         assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
   7872     }
   7873 
   7874     // https://issues.dlang.org/show_bug.cgi?id=17801
   7875     @safe unittest
   7876     {
   7877         import std.conv : to;
   7878         import std.meta : AliasSeq;
   7879         static foreach (C; AliasSeq!(char, wchar, dchar))
   7880         {
   7881             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   7882                 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
   7883         }
   7884     }
   7885 
   7886 
   7887     /++
   7888         Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
   7889         Whitespace is stripped from the given string.
   7890 
   7891         Params:
   7892             simpleString = A string formatted in the way that toSimpleString
   7893                            formats dates.
   7894 
   7895         Throws:
   7896             $(REF DateTimeException,std,datetime,date) if the given string is
   7897             not in the correct format or if the resulting $(LREF Date) would not
   7898             be valid.
   7899       +/
   7900     static Date fromSimpleString(S)(scope const S simpleString) @safe pure
   7901         if (isSomeString!(S))
   7902     {
   7903         import std.algorithm.searching : startsWith;
   7904         import std.conv : to, ConvException;
   7905         import std.format : format;
   7906         import std.string : strip;
   7907 
   7908         auto str = strip(simpleString);
   7909 
   7910         if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
   7911             throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
   7912 
   7913         int year;
   7914         uint day;
   7915         auto month = monthFromString(str[$ - 6 .. $ - 3]);
   7916         auto yearStr = str[0 .. $ - 7];
   7917         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
   7918         if ((yearStr.length > 4) != signAtBegining)
   7919         {
   7920             throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
   7921         }
   7922 
   7923         try
   7924         {
   7925             day = to!uint(str[$ - 2 .. $]);
   7926             year = to!int(yearStr);
   7927         }
   7928         catch (ConvException)
   7929         {
   7930             throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
   7931         }
   7932 
   7933         return Date(year, month, day);
   7934     }
   7935 
   7936     ///
   7937     @safe unittest
   7938     {
   7939         assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
   7940         assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
   7941         assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
   7942         assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
   7943         assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
   7944     }
   7945 
   7946     @safe unittest
   7947     {
   7948         assertThrown!DateTimeException(Date.fromSimpleString(""));
   7949         assertThrown!DateTimeException(Date.fromSimpleString("990704"));
   7950         assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
   7951         assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
   7952         assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
   7953         assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
   7954         assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
   7955         assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
   7956         assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
   7957         assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
   7958         assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
   7959 
   7960         assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
   7961         assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
   7962         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
   7963         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
   7964         assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
   7965         assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
   7966         assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
   7967         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
   7968         assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
   7969         assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
   7970         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
   7971         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
   7972         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
   7973         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
   7974         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
   7975         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
   7976         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
   7977 
   7978         assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
   7979         assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
   7980         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
   7981         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
   7982         assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
   7983         assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
   7984         assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
   7985         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
   7986         assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
   7987         assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
   7988 
   7989         assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
   7990         assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
   7991         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
   7992         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
   7993         assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
   7994         assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
   7995         assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
   7996         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
   7997         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
   7998         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
   7999         assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
   8000 
   8001         assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
   8002         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
   8003 
   8004         assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
   8005         assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
   8006         assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
   8007         assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
   8008         assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
   8009         assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
   8010     }
   8011 
   8012     // https://issues.dlang.org/show_bug.cgi?id=17801
   8013     @safe unittest
   8014     {
   8015         import std.conv : to;
   8016         import std.meta : AliasSeq;
   8017         static foreach (C; AliasSeq!(char, wchar, dchar))
   8018         {
   8019             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   8020                 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
   8021         }
   8022     }
   8023 
   8024 
   8025     /++
   8026         Returns the $(LREF Date) farthest in the past which is representable by
   8027         $(LREF Date).
   8028       +/
   8029     @property static Date min() @safe pure nothrow @nogc
   8030     {
   8031         auto date = Date.init;
   8032         date._year = short.min;
   8033         date._month = Month.jan;
   8034         date._day = 1;
   8035 
   8036         return date;
   8037     }
   8038 
   8039     @safe unittest
   8040     {
   8041         assert(Date.min.year < 0);
   8042         assert(Date.min < Date.max);
   8043     }
   8044 
   8045 
   8046     /++
   8047         Returns the $(LREF Date) farthest in the future which is representable
   8048         by $(LREF Date).
   8049       +/
   8050     @property static Date max() @safe pure nothrow @nogc
   8051     {
   8052         auto date = Date.init;
   8053         date._year = short.max;
   8054         date._month = Month.dec;
   8055         date._day = 31;
   8056 
   8057         return date;
   8058     }
   8059 
   8060     @safe unittest
   8061     {
   8062         assert(Date.max.year > 0);
   8063         assert(Date.max > Date.min);
   8064     }
   8065 
   8066 
   8067 private:
   8068 
   8069     /+
   8070         Whether the given values form a valid date.
   8071 
   8072         Params:
   8073             year  = The year to test.
   8074             month = The month of the Gregorian Calendar to test.
   8075             day   = The day of the month to test.
   8076      +/
   8077     static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
   8078     {
   8079         if (!valid!"months"(month))
   8080             return false;
   8081         return valid!"days"(year, month, day);
   8082     }
   8083 
   8084 
   8085 package:
   8086 
   8087     /+
   8088         Adds the given number of days to this $(LREF Date). A negative number
   8089         will subtract.
   8090 
   8091         The month will be adjusted along with the day if the number of days
   8092         added (or subtracted) would overflow (or underflow) the current month.
   8093         The year will be adjusted along with the month if the increase (or
   8094         decrease) to the month would cause it to overflow (or underflow) the
   8095         current year.
   8096 
   8097         `_addDays(numDays)` is effectively equivalent to
   8098         $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
   8099 
   8100         Params:
   8101             days = The number of days to add to this Date.
   8102       +/
   8103     ref Date _addDays(long days) return @safe pure nothrow @nogc
   8104     {
   8105         dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
   8106         return this;
   8107     }
   8108 
   8109     @safe unittest
   8110     {
   8111         // Test A.D.
   8112         {
   8113             auto date = Date(1999, 2, 28);
   8114             date._addDays(1);
   8115             assert(date == Date(1999, 3, 1));
   8116             date._addDays(-1);
   8117             assert(date == Date(1999, 2, 28));
   8118         }
   8119 
   8120         {
   8121             auto date = Date(2000, 2, 28);
   8122             date._addDays(1);
   8123             assert(date == Date(2000, 2, 29));
   8124             date._addDays(1);
   8125             assert(date == Date(2000, 3, 1));
   8126             date._addDays(-1);
   8127             assert(date == Date(2000, 2, 29));
   8128         }
   8129 
   8130         {
   8131             auto date = Date(1999, 6, 30);
   8132             date._addDays(1);
   8133             assert(date == Date(1999, 7, 1));
   8134             date._addDays(-1);
   8135             assert(date == Date(1999, 6, 30));
   8136         }
   8137 
   8138         {
   8139             auto date = Date(1999, 7, 31);
   8140             date._addDays(1);
   8141             assert(date == Date(1999, 8, 1));
   8142             date._addDays(-1);
   8143             assert(date == Date(1999, 7, 31));
   8144         }
   8145 
   8146         {
   8147             auto date = Date(1999, 1, 1);
   8148             date._addDays(-1);
   8149             assert(date == Date(1998, 12, 31));
   8150             date._addDays(1);
   8151             assert(date == Date(1999, 1, 1));
   8152         }
   8153 
   8154         {
   8155             auto date = Date(1999, 7, 6);
   8156             date._addDays(9);
   8157             assert(date == Date(1999, 7, 15));
   8158             date._addDays(-11);
   8159             assert(date == Date(1999, 7, 4));
   8160             date._addDays(30);
   8161             assert(date == Date(1999, 8, 3));
   8162             date._addDays(-3);
   8163             assert(date == Date(1999, 7, 31));
   8164         }
   8165 
   8166         {
   8167             auto date = Date(1999, 7, 6);
   8168             date._addDays(365);
   8169             assert(date == Date(2000, 7, 5));
   8170             date._addDays(-365);
   8171             assert(date == Date(1999, 7, 6));
   8172             date._addDays(366);
   8173             assert(date == Date(2000, 7, 6));
   8174             date._addDays(730);
   8175             assert(date == Date(2002, 7, 6));
   8176             date._addDays(-1096);
   8177             assert(date == Date(1999, 7, 6));
   8178         }
   8179 
   8180         // Test B.C.
   8181         {
   8182             auto date = Date(-1999, 2, 28);
   8183             date._addDays(1);
   8184             assert(date == Date(-1999, 3, 1));
   8185             date._addDays(-1);
   8186             assert(date == Date(-1999, 2, 28));
   8187         }
   8188 
   8189         {
   8190             auto date = Date(-2000, 2, 28);
   8191             date._addDays(1);
   8192             assert(date == Date(-2000, 2, 29));
   8193             date._addDays(1);
   8194             assert(date == Date(-2000, 3, 1));
   8195             date._addDays(-1);
   8196             assert(date == Date(-2000, 2, 29));
   8197         }
   8198 
   8199         {
   8200             auto date = Date(-1999, 6, 30);
   8201             date._addDays(1);
   8202             assert(date == Date(-1999, 7, 1));
   8203             date._addDays(-1);
   8204             assert(date == Date(-1999, 6, 30));
   8205         }
   8206 
   8207         {
   8208             auto date = Date(-1999, 7, 31);
   8209             date._addDays(1);
   8210             assert(date == Date(-1999, 8, 1));
   8211             date._addDays(-1);
   8212             assert(date == Date(-1999, 7, 31));
   8213         }
   8214 
   8215         {
   8216             auto date = Date(-1999, 1, 1);
   8217             date._addDays(-1);
   8218             assert(date == Date(-2000, 12, 31));
   8219             date._addDays(1);
   8220             assert(date == Date(-1999, 1, 1));
   8221         }
   8222 
   8223         {
   8224             auto date = Date(-1999, 7, 6);
   8225             date._addDays(9);
   8226             assert(date == Date(-1999, 7, 15));
   8227             date._addDays(-11);
   8228             assert(date == Date(-1999, 7, 4));
   8229             date._addDays(30);
   8230             assert(date == Date(-1999, 8, 3));
   8231             date._addDays(-3);
   8232         }
   8233 
   8234         {
   8235             auto date = Date(-1999, 7, 6);
   8236             date._addDays(365);
   8237             assert(date == Date(-1998, 7, 6));
   8238             date._addDays(-365);
   8239             assert(date == Date(-1999, 7, 6));
   8240             date._addDays(366);
   8241             assert(date == Date(-1998, 7, 7));
   8242             date._addDays(730);
   8243             assert(date == Date(-1996, 7, 6));
   8244             date._addDays(-1096);
   8245             assert(date == Date(-1999, 7, 6));
   8246         }
   8247 
   8248         // Test Both
   8249         {
   8250             auto date = Date(1, 7, 6);
   8251             date._addDays(-365);
   8252             assert(date == Date(0, 7, 6));
   8253             date._addDays(365);
   8254             assert(date == Date(1, 7, 6));
   8255             date._addDays(-731);
   8256             assert(date == Date(-1, 7, 6));
   8257             date._addDays(730);
   8258             assert(date == Date(1, 7, 5));
   8259         }
   8260 
   8261         const cdate = Date(1999, 7, 6);
   8262         immutable idate = Date(1999, 7, 6);
   8263         static assert(!__traits(compiles, cdate._addDays(12)));
   8264         static assert(!__traits(compiles, idate._addDays(12)));
   8265     }
   8266 
   8267 
   8268     @safe pure invariant()
   8269     {
   8270         import std.format : format;
   8271         assert(valid!"months"(_month),
   8272                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
   8273         assert(valid!"days"(_year, _month, _day),
   8274                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
   8275     }
   8276 
   8277     short _year  = 1;
   8278     Month _month = Month.jan;
   8279     ubyte _day   = 1;
   8280 }
   8281 
   8282 ///
   8283 @safe pure unittest
   8284 {
   8285     import core.time : days;
   8286 
   8287     auto d = Date(2000, 6, 1);
   8288 
   8289     assert(d.dayOfYear == 153);
   8290     assert(d.dayOfWeek == DayOfWeek.thu);
   8291 
   8292     d += 10.days;
   8293     assert(d == Date(2000, 6, 11));
   8294 
   8295     assert(d.toISOExtString() == "2000-06-11");
   8296     assert(d.toISOString() == "20000611");
   8297     assert(d.toSimpleString() == "2000-Jun-11");
   8298 
   8299     assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
   8300     assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
   8301     assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
   8302 }
   8303 
   8304 
   8305 /++
   8306     Represents a time of day with hours, minutes, and seconds. It uses 24 hour
   8307     time.
   8308 +/
   8309 struct TimeOfDay
   8310 {
   8311 public:
   8312 
   8313     /++
   8314         Params:
   8315             hour   = Hour of the day [0 - 24$(RPAREN).
   8316             minute = Minute of the hour [0 - 60$(RPAREN).
   8317             second = Second of the minute [0 - 60$(RPAREN).
   8318 
   8319         Throws:
   8320             $(REF DateTimeException,std,datetime,date) if the resulting
   8321             $(LREF TimeOfDay) would be not be valid.
   8322      +/
   8323     this(int hour, int minute, int second = 0) @safe pure
   8324     {
   8325         enforceValid!"hours"(hour);
   8326         enforceValid!"minutes"(minute);
   8327         enforceValid!"seconds"(second);
   8328 
   8329         _hour   = cast(ubyte) hour;
   8330         _minute = cast(ubyte) minute;
   8331         _second = cast(ubyte) second;
   8332     }
   8333 
   8334     @safe unittest
   8335     {
   8336         assert(TimeOfDay(0, 0) == TimeOfDay.init);
   8337 
   8338         {
   8339             auto tod = TimeOfDay(0, 0);
   8340             assert(tod._hour == 0);
   8341             assert(tod._minute == 0);
   8342             assert(tod._second == 0);
   8343         }
   8344 
   8345         {
   8346             auto tod = TimeOfDay(12, 30, 33);
   8347             assert(tod._hour == 12);
   8348             assert(tod._minute == 30);
   8349             assert(tod._second == 33);
   8350         }
   8351 
   8352         {
   8353             auto tod = TimeOfDay(23, 59, 59);
   8354             assert(tod._hour == 23);
   8355             assert(tod._minute == 59);
   8356             assert(tod._second == 59);
   8357         }
   8358 
   8359         assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
   8360         assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
   8361         assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
   8362     }
   8363 
   8364 
   8365     /++
   8366         Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
   8367 
   8368         Returns:
   8369             $(BOOKTABLE,
   8370             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
   8371             $(TR $(TD this == rhs) $(TD 0))
   8372             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
   8373             )
   8374      +/
   8375     int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
   8376     {
   8377         if (_hour < rhs._hour)
   8378             return -1;
   8379         if (_hour > rhs._hour)
   8380             return 1;
   8381 
   8382         if (_minute < rhs._minute)
   8383             return -1;
   8384         if (_minute > rhs._minute)
   8385             return 1;
   8386 
   8387         if (_second < rhs._second)
   8388             return -1;
   8389         if (_second > rhs._second)
   8390             return 1;
   8391 
   8392         return 0;
   8393     }
   8394 
   8395     @safe unittest
   8396     {
   8397         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
   8398 
   8399         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
   8400         assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
   8401         assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
   8402         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
   8403 
   8404         assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
   8405         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
   8406 
   8407         assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
   8408         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
   8409 
   8410         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
   8411         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
   8412         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
   8413         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
   8414         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
   8415         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
   8416 
   8417         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
   8418         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
   8419         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
   8420         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
   8421 
   8422         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
   8423         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
   8424 
   8425         const ctod = TimeOfDay(12, 30, 33);
   8426         immutable itod = TimeOfDay(12, 30, 33);
   8427         assert(ctod.opCmp(itod) == 0);
   8428         assert(itod.opCmp(ctod) == 0);
   8429     }
   8430 
   8431 
   8432     /++
   8433         Hours past midnight.
   8434      +/
   8435     @property ubyte hour() const @safe pure nothrow @nogc
   8436     {
   8437         return _hour;
   8438     }
   8439 
   8440     @safe unittest
   8441     {
   8442         assert(TimeOfDay.init.hour == 0);
   8443         assert(TimeOfDay(12, 0, 0).hour == 12);
   8444 
   8445         const ctod = TimeOfDay(12, 0, 0);
   8446         immutable itod = TimeOfDay(12, 0, 0);
   8447         assert(ctod.hour == 12);
   8448         assert(itod.hour == 12);
   8449     }
   8450 
   8451 
   8452     /++
   8453         Hours past midnight.
   8454 
   8455         Params:
   8456             hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
   8457 
   8458         Throws:
   8459             $(REF DateTimeException,std,datetime,date) if the given hour would
   8460             result in an invalid $(LREF TimeOfDay).
   8461      +/
   8462     @property void hour(int hour) @safe pure
   8463     {
   8464         enforceValid!"hours"(hour);
   8465         _hour = cast(ubyte) hour;
   8466     }
   8467 
   8468     @safe unittest
   8469     {
   8470         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
   8471 
   8472         auto tod = TimeOfDay(0, 0, 0);
   8473         tod.hour = 12;
   8474         assert(tod == TimeOfDay(12, 0, 0));
   8475 
   8476         const ctod = TimeOfDay(0, 0, 0);
   8477         immutable itod = TimeOfDay(0, 0, 0);
   8478         static assert(!__traits(compiles, ctod.hour = 12));
   8479         static assert(!__traits(compiles, itod.hour = 12));
   8480     }
   8481 
   8482 
   8483     /++
   8484         Minutes past the hour.
   8485      +/
   8486     @property ubyte minute() const @safe pure nothrow @nogc
   8487     {
   8488         return _minute;
   8489     }
   8490 
   8491     @safe unittest
   8492     {
   8493         assert(TimeOfDay.init.minute == 0);
   8494         assert(TimeOfDay(0, 30, 0).minute == 30);
   8495 
   8496         const ctod = TimeOfDay(0, 30, 0);
   8497         immutable itod = TimeOfDay(0, 30, 0);
   8498         assert(ctod.minute == 30);
   8499         assert(itod.minute == 30);
   8500     }
   8501 
   8502 
   8503     /++
   8504         Minutes past the hour.
   8505 
   8506         Params:
   8507             minute = The minute to set this $(LREF TimeOfDay)'s minute to.
   8508 
   8509         Throws:
   8510             $(REF DateTimeException,std,datetime,date) if the given minute
   8511             would result in an invalid $(LREF TimeOfDay).
   8512      +/
   8513     @property void minute(int minute) @safe pure
   8514     {
   8515         enforceValid!"minutes"(minute);
   8516         _minute = cast(ubyte) minute;
   8517     }
   8518 
   8519     @safe unittest
   8520     {
   8521         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
   8522 
   8523         auto tod = TimeOfDay(0, 0, 0);
   8524         tod.minute = 30;
   8525         assert(tod == TimeOfDay(0, 30, 0));
   8526 
   8527         const ctod = TimeOfDay(0, 0, 0);
   8528         immutable itod = TimeOfDay(0, 0, 0);
   8529         static assert(!__traits(compiles, ctod.minute = 30));
   8530         static assert(!__traits(compiles, itod.minute = 30));
   8531     }
   8532 
   8533 
   8534     /++
   8535         Seconds past the minute.
   8536      +/
   8537     @property ubyte second() const @safe pure nothrow @nogc
   8538     {
   8539         return _second;
   8540     }
   8541 
   8542     @safe unittest
   8543     {
   8544         assert(TimeOfDay.init.second == 0);
   8545         assert(TimeOfDay(0, 0, 33).second == 33);
   8546 
   8547         const ctod = TimeOfDay(0, 0, 33);
   8548         immutable itod = TimeOfDay(0, 0, 33);
   8549         assert(ctod.second == 33);
   8550         assert(itod.second == 33);
   8551     }
   8552 
   8553 
   8554     /++
   8555         Seconds past the minute.
   8556 
   8557         Params:
   8558             second = The second to set this $(LREF TimeOfDay)'s second to.
   8559 
   8560         Throws:
   8561             $(REF DateTimeException,std,datetime,date) if the given second
   8562             would result in an invalid $(LREF TimeOfDay).
   8563      +/
   8564     @property void second(int second) @safe pure
   8565     {
   8566         enforceValid!"seconds"(second);
   8567         _second = cast(ubyte) second;
   8568     }
   8569 
   8570     @safe unittest
   8571     {
   8572         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
   8573 
   8574         auto tod = TimeOfDay(0, 0, 0);
   8575         tod.second = 33;
   8576         assert(tod == TimeOfDay(0, 0, 33));
   8577 
   8578         const ctod = TimeOfDay(0, 0, 0);
   8579         immutable itod = TimeOfDay(0, 0, 0);
   8580         static assert(!__traits(compiles, ctod.second = 33));
   8581         static assert(!__traits(compiles, itod.second = 33));
   8582     }
   8583 
   8584 
   8585     /++
   8586         Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
   8587         negative number will subtract.
   8588 
   8589         The difference between rolling and adding is that rolling does not
   8590         affect larger units. For instance, rolling a $(LREF TimeOfDay)
   8591         one hours's worth of minutes gets the exact same
   8592         $(LREF TimeOfDay).
   8593 
   8594         Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
   8595 
   8596         Params:
   8597             units = The units to add.
   8598             value = The number of $(D_PARAM units) to add to this
   8599                     $(LREF TimeOfDay).
   8600 
   8601         Returns:
   8602             A reference to the `TimeOfDay` (`this`).
   8603       +/
   8604     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
   8605         if (units == "hours")
   8606     {
   8607         import core.time : dur;
   8608         return this += dur!"hours"(value);
   8609     }
   8610 
   8611     ///
   8612     @safe unittest
   8613     {
   8614         auto tod1 = TimeOfDay(7, 12, 0);
   8615         tod1.roll!"hours"(1);
   8616         assert(tod1 == TimeOfDay(8, 12, 0));
   8617 
   8618         auto tod2 = TimeOfDay(7, 12, 0);
   8619         tod2.roll!"hours"(-1);
   8620         assert(tod2 == TimeOfDay(6, 12, 0));
   8621 
   8622         auto tod3 = TimeOfDay(23, 59, 0);
   8623         tod3.roll!"minutes"(1);
   8624         assert(tod3 == TimeOfDay(23, 0, 0));
   8625 
   8626         auto tod4 = TimeOfDay(0, 0, 0);
   8627         tod4.roll!"minutes"(-1);
   8628         assert(tod4 == TimeOfDay(0, 59, 0));
   8629 
   8630         auto tod5 = TimeOfDay(23, 59, 59);
   8631         tod5.roll!"seconds"(1);
   8632         assert(tod5 == TimeOfDay(23, 59, 0));
   8633 
   8634         auto tod6 = TimeOfDay(0, 0, 0);
   8635         tod6.roll!"seconds"(-1);
   8636         assert(tod6 == TimeOfDay(0, 0, 59));
   8637     }
   8638 
   8639     @safe unittest
   8640     {
   8641         auto tod = TimeOfDay(12, 27, 2);
   8642         tod.roll!"hours"(22).roll!"hours"(-7);
   8643         assert(tod == TimeOfDay(3, 27, 2));
   8644 
   8645         const ctod = TimeOfDay(0, 0, 0);
   8646         immutable itod = TimeOfDay(0, 0, 0);
   8647         static assert(!__traits(compiles, ctod.roll!"hours"(53)));
   8648         static assert(!__traits(compiles, itod.roll!"hours"(53)));
   8649     }
   8650 
   8651 
   8652     /// ditto
   8653     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
   8654         if (units == "minutes" || units == "seconds")
   8655     {
   8656         import std.format : format;
   8657 
   8658         enum memberVarStr = units[0 .. $ - 1];
   8659         value %= 60;
   8660         mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
   8661 
   8662         if (value < 0)
   8663         {
   8664             if (newVal < 0)
   8665                 newVal += 60;
   8666         }
   8667         else if (newVal >= 60)
   8668             newVal -= 60;
   8669 
   8670         mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
   8671         return this;
   8672     }
   8673 
   8674     // Test roll!"minutes"().
   8675     @safe unittest
   8676     {
   8677         static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
   8678         {
   8679             orig.roll!"minutes"(minutes);
   8680             assert(orig == expected);
   8681         }
   8682 
   8683         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
   8684         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
   8685         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
   8686         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
   8687         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
   8688         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
   8689         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
   8690         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
   8691         testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
   8692         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
   8693         testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
   8694         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
   8695         testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
   8696         testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
   8697         testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
   8698 
   8699         testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
   8700         testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
   8701         testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
   8702         testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
   8703         testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
   8704         testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
   8705         testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
   8706         testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
   8707 
   8708         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
   8709         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
   8710         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
   8711         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
   8712         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
   8713         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
   8714         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
   8715         testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
   8716         testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
   8717         testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
   8718         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
   8719         testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
   8720         testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
   8721         testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
   8722 
   8723         testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
   8724         testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
   8725         testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
   8726         testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
   8727         testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
   8728         testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
   8729         testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
   8730         testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
   8731 
   8732         testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
   8733         testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
   8734         testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
   8735 
   8736         testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
   8737         testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
   8738         testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
   8739 
   8740         testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
   8741         testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
   8742         testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
   8743 
   8744         testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
   8745         testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
   8746         testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
   8747 
   8748         auto tod = TimeOfDay(12, 27, 2);
   8749         tod.roll!"minutes"(97).roll!"minutes"(-102);
   8750         assert(tod == TimeOfDay(12, 22, 2));
   8751 
   8752         const ctod = TimeOfDay(0, 0, 0);
   8753         immutable itod = TimeOfDay(0, 0, 0);
   8754         static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
   8755         static assert(!__traits(compiles, itod.roll!"minutes"(7)));
   8756     }
   8757 
   8758     // Test roll!"seconds"().
   8759     @safe unittest
   8760     {
   8761         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
   8762         {
   8763             orig.roll!"seconds"(seconds);
   8764             assert(orig == expected);
   8765         }
   8766 
   8767         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
   8768         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
   8769         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
   8770         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
   8771         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
   8772         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
   8773         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
   8774         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
   8775         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
   8776         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
   8777         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
   8778         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
   8779         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
   8780         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
   8781 
   8782         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
   8783         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
   8784         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
   8785         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
   8786         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
   8787         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
   8788         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
   8789         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
   8790 
   8791         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
   8792         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
   8793         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
   8794         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
   8795         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
   8796         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
   8797         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
   8798         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
   8799         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
   8800         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
   8801         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
   8802         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
   8803         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
   8804 
   8805         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
   8806         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
   8807         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
   8808 
   8809         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
   8810         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
   8811         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
   8812 
   8813         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
   8814         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
   8815         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
   8816 
   8817         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
   8818         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
   8819         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
   8820 
   8821         auto tod = TimeOfDay(12, 27, 2);
   8822         tod.roll!"seconds"(105).roll!"seconds"(-77);
   8823         assert(tod == TimeOfDay(12, 27, 30));
   8824 
   8825         const ctod = TimeOfDay(0, 0, 0);
   8826         immutable itod = TimeOfDay(0, 0, 0);
   8827         static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
   8828         static assert(!__traits(compiles, itod.roll!"seconds"(7)));
   8829     }
   8830 
   8831 
   8832     import core.time : Duration;
   8833     /++
   8834         Gives the result of adding or subtracting a $(REF Duration, core,time)
   8835         from this $(LREF TimeOfDay).
   8836 
   8837         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
   8838         are
   8839 
   8840         $(BOOKTABLE,
   8841         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
   8842         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
   8843         )
   8844 
   8845         Params:
   8846             duration = The $(REF Duration, core,time) to add to or subtract from
   8847                        this $(LREF TimeOfDay).
   8848       +/
   8849     TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
   8850         if (op == "+" || op == "-")
   8851     {
   8852         TimeOfDay retval = this;
   8853         immutable seconds = duration.total!"seconds";
   8854         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
   8855     }
   8856 
   8857     ///
   8858     @safe unittest
   8859     {
   8860         import core.time : hours, minutes, seconds;
   8861 
   8862         assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
   8863         assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
   8864         assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
   8865         assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
   8866 
   8867         assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
   8868         assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
   8869         assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
   8870         assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
   8871     }
   8872 
   8873     @safe unittest
   8874     {
   8875         auto tod = TimeOfDay(12, 30, 33);
   8876 
   8877         import core.time : dur;
   8878         assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
   8879         assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
   8880         assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
   8881         assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
   8882         assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
   8883         assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
   8884 
   8885         assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
   8886         assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
   8887         assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
   8888         assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
   8889         assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
   8890         assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
   8891 
   8892         assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
   8893         assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
   8894         assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
   8895         assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
   8896         assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
   8897         assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
   8898 
   8899         assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
   8900         assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
   8901         assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
   8902         assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
   8903         assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
   8904         assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
   8905 
   8906         auto duration = dur!"hours"(11);
   8907         const ctod = TimeOfDay(12, 30, 33);
   8908         immutable itod = TimeOfDay(12, 30, 33);
   8909         assert(tod + duration == TimeOfDay(23, 30, 33));
   8910         assert(ctod + duration == TimeOfDay(23, 30, 33));
   8911         assert(itod + duration == TimeOfDay(23, 30, 33));
   8912 
   8913         assert(tod - duration == TimeOfDay(1, 30, 33));
   8914         assert(ctod - duration == TimeOfDay(1, 30, 33));
   8915         assert(itod - duration == TimeOfDay(1, 30, 33));
   8916     }
   8917 
   8918 
   8919     /++
   8920         Gives the result of adding or subtracting a $(REF Duration, core,time)
   8921         from this $(LREF TimeOfDay), as well as assigning the result to this
   8922         $(LREF TimeOfDay).
   8923 
   8924         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
   8925         are
   8926 
   8927         $(BOOKTABLE,
   8928         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
   8929         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
   8930         )
   8931 
   8932         Params:
   8933             duration = The $(REF Duration, core,time) to add to or subtract from
   8934                        this $(LREF TimeOfDay).
   8935       +/
   8936     ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
   8937         if (op == "+" || op == "-")
   8938     {
   8939         immutable seconds = duration.total!"seconds";
   8940         mixin("return _addSeconds(" ~ op ~ "seconds);");
   8941     }
   8942 
   8943     @safe unittest
   8944     {
   8945         import core.time : dur;
   8946         auto duration = dur!"hours"(12);
   8947 
   8948         assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
   8949         assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
   8950         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
   8951         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
   8952         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
   8953         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
   8954 
   8955         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
   8956         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
   8957         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
   8958         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
   8959         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
   8960         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
   8961 
   8962         assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
   8963         assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
   8964         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
   8965         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
   8966         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
   8967         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
   8968 
   8969         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
   8970         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
   8971         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
   8972         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
   8973         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
   8974         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
   8975 
   8976         auto tod = TimeOfDay(19, 17, 22);
   8977         (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
   8978         assert(tod == TimeOfDay(17, 15, 59));
   8979 
   8980         const ctod = TimeOfDay(12, 33, 30);
   8981         immutable itod = TimeOfDay(12, 33, 30);
   8982         static assert(!__traits(compiles, ctod += duration));
   8983         static assert(!__traits(compiles, itod += duration));
   8984         static assert(!__traits(compiles, ctod -= duration));
   8985         static assert(!__traits(compiles, itod -= duration));
   8986     }
   8987 
   8988 
   8989     /++
   8990         Gives the difference between two $(LREF TimeOfDay)s.
   8991 
   8992         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
   8993         are
   8994 
   8995         $(BOOKTABLE,
   8996         $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
   8997         )
   8998 
   8999         Params:
   9000             rhs = The $(LREF TimeOfDay) to subtract from this one.
   9001       +/
   9002     Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
   9003         if (op == "-")
   9004     {
   9005         immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
   9006         immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
   9007 
   9008         import core.time : dur;
   9009         return dur!"seconds"(lhsSec - rhsSec);
   9010     }
   9011 
   9012     @safe unittest
   9013     {
   9014         auto tod = TimeOfDay(12, 30, 33);
   9015 
   9016         import core.time : dur;
   9017         assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
   9018         assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
   9019         assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
   9020         assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
   9021         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
   9022         assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
   9023         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
   9024         assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
   9025 
   9026         const ctod = TimeOfDay(12, 30, 33);
   9027         immutable itod = TimeOfDay(12, 30, 33);
   9028         assert(tod - tod == Duration.zero);
   9029         assert(ctod - tod == Duration.zero);
   9030         assert(itod - tod == Duration.zero);
   9031 
   9032         assert(tod - ctod == Duration.zero);
   9033         assert(ctod - ctod == Duration.zero);
   9034         assert(itod - ctod == Duration.zero);
   9035 
   9036         assert(tod - itod == Duration.zero);
   9037         assert(ctod - itod == Duration.zero);
   9038         assert(itod - itod == Duration.zero);
   9039     }
   9040 
   9041 
   9042     /++
   9043         Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
   9044         If `writer` is set, the resulting string will be written directly to it.
   9045 
   9046         Params:
   9047             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   9048         Returns:
   9049             A `string` when not using an output range; `void` otherwise.
   9050       +/
   9051     string toISOString() const @safe pure nothrow
   9052     {
   9053         import std.array : appender;
   9054         auto w = appender!string();
   9055         w.reserve(6);
   9056         try
   9057             toISOString(w);
   9058         catch (Exception e)
   9059             assert(0, "toISOString() threw.");
   9060         return w.data;
   9061     }
   9062 
   9063     /// ditto
   9064     void toISOString(W)(ref W writer) const
   9065     if (isOutputRange!(W, char))
   9066     {
   9067         import std.format.write : formattedWrite;
   9068         formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
   9069     }
   9070 
   9071     ///
   9072     @safe unittest
   9073     {
   9074         assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
   9075         assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
   9076     }
   9077 
   9078     @safe unittest
   9079     {
   9080         auto tod = TimeOfDay(12, 30, 33);
   9081         const ctod = TimeOfDay(12, 30, 33);
   9082         immutable itod = TimeOfDay(12, 30, 33);
   9083         assert(tod.toISOString() == "123033");
   9084         assert(ctod.toISOString() == "123033");
   9085         assert(itod.toISOString() == "123033");
   9086     }
   9087 
   9088 
   9089     /++
   9090         Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
   9091         If `writer` is set, the resulting string will be written directly to it.
   9092 
   9093         Params:
   9094             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   9095         Returns:
   9096             A `string` when not using an output range; `void` otherwise.
   9097       +/
   9098     string toISOExtString() const @safe pure nothrow
   9099     {
   9100         import std.array : appender;
   9101         auto w = appender!string();
   9102         w.reserve(8);
   9103         try
   9104             toISOExtString(w);
   9105         catch (Exception e)
   9106             assert(0, "toISOExtString() threw.");
   9107         return w.data;
   9108     }
   9109 
   9110     /// ditto
   9111     void toISOExtString(W)(ref W writer) const
   9112     if (isOutputRange!(W, char))
   9113     {
   9114         import std.format.write : formattedWrite;
   9115         formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
   9116     }
   9117 
   9118     ///
   9119     @safe unittest
   9120     {
   9121         assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
   9122         assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
   9123     }
   9124 
   9125     @safe unittest
   9126     {
   9127         auto tod = TimeOfDay(12, 30, 33);
   9128         const ctod = TimeOfDay(12, 30, 33);
   9129         immutable itod = TimeOfDay(12, 30, 33);
   9130         assert(tod.toISOExtString() == "12:30:33");
   9131         assert(ctod.toISOExtString() == "12:30:33");
   9132         assert(itod.toISOExtString() == "12:30:33");
   9133     }
   9134 
   9135 
   9136     /++
   9137         Converts this TimeOfDay to a string.
   9138 
   9139         This function exists to make it easy to convert a $(LREF TimeOfDay) to a
   9140         string for code that does not care what the exact format is - just that
   9141         it presents the information in a clear manner. It also makes it easy to
   9142         simply convert a $(LREF TimeOfDay) to a string when using functions such
   9143         as `to!string`, `format`, or `writeln` which use toString to convert
   9144         user-defined types. So, it is unlikely that much code will call
   9145         toString directly.
   9146 
   9147         The format of the string is purposefully unspecified, and code that
   9148         cares about the format of the string should use `toISOString`,
   9149         `toISOExtString`, or some other custom formatting function that
   9150         explicitly generates the format that the code needs. The reason is that
   9151         the code is then clear about what format it's using, making it less
   9152         error-prone to maintain the code and interact with other software that
   9153         consumes the generated strings. It's for this same reason that
   9154         $(LREF TimeOfDay) has no `fromString` function, whereas it does have
   9155         `fromISOString` and `fromISOExtString`.
   9156 
   9157         The format returned by toString may or may not change in the future.
   9158 
   9159         Params:
   9160             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
   9161         Returns:
   9162             A `string` when not using an output range; `void` otherwise.
   9163       +/
   9164     string toString() const @safe pure nothrow
   9165     {
   9166         return toISOExtString();
   9167     }
   9168 
   9169     /// ditto
   9170     void toString(W)(ref W writer) const
   9171     if (isOutputRange!(W, char))
   9172     {
   9173         toISOExtString(writer);
   9174     }
   9175 
   9176     @safe unittest
   9177     {
   9178         auto tod = TimeOfDay(12, 30, 33);
   9179         const ctod = TimeOfDay(12, 30, 33);
   9180         immutable itod = TimeOfDay(12, 30, 33);
   9181         assert(tod.toString());
   9182         assert(ctod.toString());
   9183         assert(itod.toString());
   9184     }
   9185 
   9186 
   9187     /++
   9188         Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
   9189         Whitespace is stripped from the given string.
   9190 
   9191         Params:
   9192             isoString = A string formatted in the ISO format for times.
   9193 
   9194         Throws:
   9195             $(REF DateTimeException,std,datetime,date) if the given string is
   9196             not in the ISO format or if the resulting $(LREF TimeOfDay) would
   9197             not be valid.
   9198       +/
   9199     static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
   9200         if (isSomeString!S)
   9201     {
   9202         import std.conv : to, text, ConvException;
   9203         import std.exception : enforce;
   9204         import std.string : strip;
   9205 
   9206         int hours, minutes, seconds;
   9207         auto str = strip(isoString);
   9208 
   9209         enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString));
   9210 
   9211         try
   9212         {
   9213             // cast to int from uint is used because it checks for
   9214             // non digits without extra loops
   9215             hours = cast(int) to!uint(str[0 .. 2]);
   9216             minutes = cast(int) to!uint(str[2 .. 4]);
   9217             seconds = cast(int) to!uint(str[4 .. $]);
   9218         }
   9219         catch (ConvException)
   9220         {
   9221             throw new DateTimeException(text("Invalid ISO String: ", isoString));
   9222         }
   9223 
   9224         return TimeOfDay(hours, minutes, seconds);
   9225     }
   9226 
   9227     ///
   9228     @safe unittest
   9229     {
   9230         assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
   9231         assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
   9232         assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
   9233     }
   9234 
   9235     @safe unittest
   9236     {
   9237         assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
   9238         assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
   9239         assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
   9240         assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
   9241         assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
   9242         assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
   9243         assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
   9244         assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
   9245         assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
   9246         assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
   9247         assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
   9248         assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
   9249         assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
   9250         assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
   9251         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
   9252         assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
   9253         assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
   9254         assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
   9255         assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
   9256         assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
   9257         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
   9258         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
   9259 
   9260         assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
   9261         assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
   9262         assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
   9263         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
   9264         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
   9265         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
   9266         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
   9267         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
   9268         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
   9269         assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
   9270         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
   9271         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
   9272         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
   9273         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
   9274         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
   9275         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
   9276         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
   9277         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
   9278         assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
   9279         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
   9280         assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
   9281         assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
   9282         assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
   9283         assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
   9284         assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
   9285         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
   9286         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
   9287 
   9288         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
   9289 
   9290         assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
   9291         assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
   9292         assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
   9293         assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
   9294         assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
   9295         assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
   9296     }
   9297 
   9298     // https://issues.dlang.org/show_bug.cgi?id=17801
   9299     @safe unittest
   9300     {
   9301         import std.conv : to;
   9302         import std.meta : AliasSeq;
   9303         static foreach (C; AliasSeq!(char, wchar, dchar))
   9304         {
   9305             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   9306                 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
   9307         }
   9308     }
   9309 
   9310 
   9311     /++
   9312         Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
   9313         Whitespace is stripped from the given string.
   9314 
   9315         Params:
   9316             isoExtString = A string formatted in the ISO Extended format for
   9317             times.
   9318 
   9319         Throws:
   9320             $(REF DateTimeException,std,datetime,date) if the given string is
   9321             not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
   9322             would not be valid.
   9323       +/
   9324     static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
   9325         if (isSomeString!S)
   9326     {
   9327         import std.conv : ConvException, text, to;
   9328         import std.string : strip;
   9329 
   9330         auto str = strip(isoExtString);
   9331         int hours, minutes, seconds;
   9332 
   9333         if (str.length != 8 || str[2] != ':' || str[5] != ':')
   9334             throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
   9335 
   9336         try
   9337         {
   9338             // cast to int from uint is used because it checks for
   9339             // non digits without extra loops
   9340             hours = cast(int) to!uint(str[0 .. 2]);
   9341             minutes = cast(int) to!uint(str[3 .. 5]);
   9342             seconds = cast(int) to!uint(str[6 .. $]);
   9343         }
   9344         catch (ConvException)
   9345         {
   9346             throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
   9347         }
   9348 
   9349         return TimeOfDay(hours, minutes, seconds);
   9350     }
   9351 
   9352     ///
   9353     @safe unittest
   9354     {
   9355         assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
   9356         assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
   9357         assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
   9358     }
   9359 
   9360     @safe unittest
   9361     {
   9362         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
   9363         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
   9364         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
   9365         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
   9366         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
   9367         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
   9368         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
   9369         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
   9370         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
   9371         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
   9372         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
   9373         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
   9374         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
   9375         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
   9376         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
   9377         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
   9378         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
   9379         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
   9380         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
   9381         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
   9382         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
   9383         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
   9384 
   9385         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
   9386         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
   9387         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
   9388         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
   9389         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
   9390         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
   9391         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
   9392         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
   9393         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
   9394         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
   9395         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
   9396         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
   9397         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
   9398         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
   9399         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
   9400         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
   9401         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
   9402         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
   9403         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
   9404         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
   9405         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
   9406         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
   9407         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
   9408         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
   9409         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
   9410         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
   9411         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
   9412 
   9413         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
   9414 
   9415         assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
   9416         assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
   9417         assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
   9418         assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
   9419         assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
   9420         assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
   9421     }
   9422 
   9423     // https://issues.dlang.org/show_bug.cgi?id=17801
   9424     @safe unittest
   9425     {
   9426         import std.conv : to;
   9427         import std.meta : AliasSeq;
   9428         static foreach (C; AliasSeq!(char, wchar, dchar))
   9429         {
   9430             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
   9431                 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
   9432         }
   9433     }
   9434 
   9435 
   9436     /++
   9437         Returns midnight.
   9438       +/
   9439     @property static TimeOfDay min() @safe pure nothrow @nogc
   9440     {
   9441         return TimeOfDay.init;
   9442     }
   9443 
   9444     @safe unittest
   9445     {
   9446         assert(TimeOfDay.min.hour == 0);
   9447         assert(TimeOfDay.min.minute == 0);
   9448         assert(TimeOfDay.min.second == 0);
   9449         assert(TimeOfDay.min < TimeOfDay.max);
   9450     }
   9451 
   9452 
   9453     /++
   9454         Returns one second short of midnight.
   9455       +/
   9456     @property static TimeOfDay max() @safe pure nothrow @nogc
   9457     {
   9458         auto tod = TimeOfDay.init;
   9459         tod._hour = maxHour;
   9460         tod._minute = maxMinute;
   9461         tod._second = maxSecond;
   9462 
   9463         return tod;
   9464     }
   9465 
   9466     @safe unittest
   9467     {
   9468         assert(TimeOfDay.max.hour == 23);
   9469         assert(TimeOfDay.max.minute == 59);
   9470         assert(TimeOfDay.max.second == 59);
   9471         assert(TimeOfDay.max > TimeOfDay.min);
   9472     }
   9473 
   9474 
   9475 private:
   9476 
   9477     /+
   9478         Add seconds to the time of day. Negative values will subtract. If the
   9479         number of seconds overflows (or underflows), then the seconds will wrap,
   9480         increasing (or decreasing) the number of minutes accordingly. If the
   9481         number of minutes overflows (or underflows), then the minutes will wrap.
   9482         If the number of minutes overflows(or underflows), then the hour will
   9483         wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
   9484 
   9485         Params:
   9486             seconds = The number of seconds to add to this TimeOfDay.
   9487       +/
   9488     ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
   9489     {
   9490         import core.time : convert;
   9491         long hnsecs = convert!("seconds", "hnsecs")(seconds);
   9492         hnsecs += convert!("hours", "hnsecs")(_hour);
   9493         hnsecs += convert!("minutes", "hnsecs")(_minute);
   9494         hnsecs += convert!("seconds", "hnsecs")(_second);
   9495 
   9496         hnsecs %= convert!("days", "hnsecs")(1);
   9497 
   9498         if (hnsecs < 0)
   9499             hnsecs += convert!("days", "hnsecs")(1);
   9500 
   9501         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
   9502         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
   9503         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
   9504 
   9505         _hour = cast(ubyte) newHours;
   9506         _minute = cast(ubyte) newMinutes;
   9507         _second = cast(ubyte) newSeconds;
   9508 
   9509         return this;
   9510     }
   9511 
   9512     @safe unittest
   9513     {
   9514         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
   9515         {
   9516             orig._addSeconds(seconds);
   9517             assert(orig == expected);
   9518         }
   9519 
   9520         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
   9521         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
   9522         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
   9523         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
   9524         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
   9525         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
   9526         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
   9527         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
   9528         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
   9529         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
   9530         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
   9531         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
   9532         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
   9533         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
   9534 
   9535         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
   9536         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
   9537         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
   9538         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
   9539         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
   9540         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
   9541         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
   9542         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
   9543 
   9544         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
   9545         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
   9546         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
   9547         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
   9548         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
   9549         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
   9550         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
   9551         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
   9552         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
   9553         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
   9554         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
   9555         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
   9556         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
   9557 
   9558         testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
   9559         testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
   9560         testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
   9561         testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
   9562         testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
   9563         testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
   9564 
   9565         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
   9566         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
   9567         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
   9568 
   9569         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
   9570         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
   9571         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
   9572 
   9573         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
   9574         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
   9575         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
   9576 
   9577         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
   9578         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
   9579         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
   9580 
   9581         const ctod = TimeOfDay(0, 0, 0);
   9582         immutable itod = TimeOfDay(0, 0, 0);
   9583         static assert(!__traits(compiles, ctod._addSeconds(7)));
   9584         static assert(!__traits(compiles, itod._addSeconds(7)));
   9585     }
   9586 
   9587 
   9588     /+
   9589         Whether the given values form a valid $(LREF TimeOfDay).
   9590      +/
   9591     static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
   9592     {
   9593         return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
   9594     }
   9595 
   9596 
   9597     @safe pure invariant()
   9598     {
   9599         import std.format : format;
   9600         assert(_valid(_hour, _minute, _second),
   9601                format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
   9602     }
   9603 
   9604 
   9605 package:
   9606 
   9607     ubyte _hour;
   9608     ubyte _minute;
   9609     ubyte _second;
   9610 
   9611     enum ubyte maxHour   = 24 - 1;
   9612     enum ubyte maxMinute = 60 - 1;
   9613     enum ubyte maxSecond = 60 - 1;
   9614 }
   9615 
   9616 ///
   9617 @safe pure unittest
   9618 {
   9619     import core.time : minutes, seconds;
   9620 
   9621     auto t = TimeOfDay(12, 30, 0);
   9622 
   9623     t += 10.minutes + 100.seconds;
   9624     assert(t == TimeOfDay(12, 41, 40));
   9625 
   9626     assert(t.toISOExtString() == "12:41:40");
   9627     assert(t.toISOString() == "124140");
   9628 
   9629     assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
   9630     assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
   9631 }
   9632 
   9633 /++
   9634     Returns whether the given value is valid for the given unit type when in a
   9635     time point. Naturally, a duration is not held to a particular range, but
   9636     the values in a time point are (e.g. a month must be in the range of
   9637     1 - 12 inclusive).
   9638 
   9639     Params:
   9640         units = The units of time to validate.
   9641         value = The number to validate.
   9642   +/
   9643 bool valid(string units)(int value) @safe pure nothrow @nogc
   9644 if (units == "months" ||
   9645     units == "hours" ||
   9646     units == "minutes" ||
   9647     units == "seconds")
   9648 {
   9649     static if (units == "months")
   9650         return value >= Month.jan && value <= Month.dec;
   9651     else static if (units == "hours")
   9652         return value >= 0 && value <= 23;
   9653     else static if (units == "minutes")
   9654         return value >= 0 && value <= 59;
   9655     else static if (units == "seconds")
   9656         return value >= 0 && value <= 59;
   9657 }
   9658 
   9659 ///
   9660 @safe unittest
   9661 {
   9662     assert(valid!"hours"(12));
   9663     assert(!valid!"hours"(32));
   9664     assert(valid!"months"(12));
   9665     assert(!valid!"months"(13));
   9666 }
   9667 
   9668 /++
   9669     Returns whether the given day is valid for the given year and month.
   9670 
   9671     Params:
   9672         units = The units of time to validate.
   9673         year  = The year of the day to validate.
   9674         month = The month of the day to validate (January is 1).
   9675         day   = The day to validate.
   9676   +/
   9677 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
   9678 if (units == "days")
   9679 {
   9680     return day > 0 && day <= maxDay(year, month);
   9681 }
   9682 
   9683 ///
   9684 @safe pure nothrow @nogc unittest
   9685 {
   9686     assert(valid!"days"(2016, 2, 29));
   9687     assert(!valid!"days"(2016, 2, 30));
   9688     assert(valid!"days"(2017, 2, 20));
   9689     assert(!valid!"days"(2017, 2, 29));
   9690 }
   9691 
   9692 
   9693 /++
   9694     Params:
   9695         units = The units of time to validate.
   9696         value = The number to validate.
   9697         file  = The file that the $(LREF DateTimeException) will list if thrown.
   9698         line  = The line number that the $(LREF DateTimeException) will list if
   9699                 thrown.
   9700 
   9701     Throws:
   9702         $(LREF DateTimeException) if `valid!units(value)` is false.
   9703   +/
   9704 void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
   9705 if (units == "months" ||
   9706     units == "hours" ||
   9707     units == "minutes" ||
   9708     units == "seconds")
   9709 {
   9710     import std.format : format;
   9711 
   9712     static if (units == "months")
   9713     {
   9714         if (!valid!units(value))
   9715             throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
   9716     }
   9717     else static if (units == "hours")
   9718     {
   9719         if (!valid!units(value))
   9720             throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
   9721     }
   9722     else static if (units == "minutes")
   9723     {
   9724         if (!valid!units(value))
   9725             throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
   9726     }
   9727     else static if (units == "seconds")
   9728     {
   9729         if (!valid!units(value))
   9730             throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
   9731     }
   9732 }
   9733 
   9734 ///
   9735 @safe pure unittest
   9736 {
   9737     import std.exception : assertThrown, assertNotThrown;
   9738 
   9739     assertNotThrown(enforceValid!"months"(10));
   9740     assertNotThrown(enforceValid!"seconds"(40));
   9741 
   9742     assertThrown!DateTimeException(enforceValid!"months"(0));
   9743     assertThrown!DateTimeException(enforceValid!"hours"(24));
   9744     assertThrown!DateTimeException(enforceValid!"minutes"(60));
   9745     assertThrown!DateTimeException(enforceValid!"seconds"(60));
   9746 }
   9747 
   9748 
   9749 /++
   9750     Because the validity of the day number depends on both on the year
   9751     and month of which the day is occurring, take all three variables
   9752     to validate the day.
   9753 
   9754     Params:
   9755         units = The units of time to validate.
   9756         year  = The year of the day to validate.
   9757         month = The month of the day to validate.
   9758         day   = The day to validate.
   9759         file  = The file that the $(LREF DateTimeException) will list if thrown.
   9760         line  = The line number that the $(LREF DateTimeException) will list if
   9761                 thrown.
   9762 
   9763     Throws:
   9764         $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
   9765   +/
   9766 void enforceValid(string units)
   9767                  (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
   9768 if (units == "days")
   9769 {
   9770     import std.format : format;
   9771     if (!valid!"days"(year, month, day))
   9772         throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
   9773 }
   9774 
   9775 ///
   9776 @safe pure unittest
   9777 {
   9778     import std.exception : assertThrown, assertNotThrown;
   9779 
   9780     assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
   9781     // leap year
   9782     assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
   9783 
   9784     assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
   9785     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
   9786     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
   9787 }
   9788 
   9789 
   9790 /++
   9791     Returns the number of days from the current day of the week to the given
   9792     day of the week. If they are the same, then the result is 0.
   9793 
   9794     Params:
   9795         currDoW = The current day of the week.
   9796         dow     = The day of the week to get the number of days to.
   9797   +/
   9798 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
   9799 {
   9800     if (currDoW == dow)
   9801         return 0;
   9802     if (currDoW < dow)
   9803         return dow - currDoW;
   9804     return DayOfWeek.sat - currDoW + dow + 1;
   9805 }
   9806 
   9807 ///
   9808 @safe pure nothrow @nogc unittest
   9809 {
   9810     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
   9811     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
   9812     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
   9813 }
   9814 
   9815 @safe unittest
   9816 {
   9817     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
   9818     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
   9819     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
   9820     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
   9821     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
   9822     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
   9823     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
   9824 
   9825     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
   9826     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
   9827     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
   9828     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
   9829     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
   9830     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
   9831     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
   9832 
   9833     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
   9834     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
   9835     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
   9836     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
   9837     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
   9838     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
   9839     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
   9840 
   9841     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
   9842     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
   9843     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
   9844     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
   9845     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
   9846     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
   9847     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
   9848 
   9849     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
   9850     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
   9851     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
   9852     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
   9853     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
   9854     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
   9855     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
   9856 
   9857     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
   9858     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
   9859     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
   9860     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
   9861     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
   9862     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
   9863     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
   9864 
   9865     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
   9866     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
   9867     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
   9868     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
   9869     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
   9870     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
   9871     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
   9872 }
   9873 
   9874 
   9875 /++
   9876     Returns the number of months from the current months of the year to the
   9877     given month of the year. If they are the same, then the result is 0.
   9878 
   9879     Params:
   9880         currMonth = The current month of the year.
   9881         month     = The month of the year to get the number of months to.
   9882   +/
   9883 int monthsToMonth(int currMonth, int month) @safe pure
   9884 {
   9885     enforceValid!"months"(currMonth);
   9886     enforceValid!"months"(month);
   9887 
   9888     if (currMonth == month)
   9889         return 0;
   9890     if (currMonth < month)
   9891         return month - currMonth;
   9892     return Month.dec - currMonth + month;
   9893 }
   9894 
   9895 ///
   9896 @safe pure unittest
   9897 {
   9898     assert(monthsToMonth(Month.jan, Month.jan) == 0);
   9899     assert(monthsToMonth(Month.jan, Month.dec) == 11);
   9900     assert(monthsToMonth(Month.jul, Month.oct) == 3);
   9901 }
   9902 
   9903 @safe unittest
   9904 {
   9905     assert(monthsToMonth(Month.jan, Month.jan) == 0);
   9906     assert(monthsToMonth(Month.jan, Month.feb) == 1);
   9907     assert(monthsToMonth(Month.jan, Month.mar) == 2);
   9908     assert(monthsToMonth(Month.jan, Month.apr) == 3);
   9909     assert(monthsToMonth(Month.jan, Month.may) == 4);
   9910     assert(monthsToMonth(Month.jan, Month.jun) == 5);
   9911     assert(monthsToMonth(Month.jan, Month.jul) == 6);
   9912     assert(monthsToMonth(Month.jan, Month.aug) == 7);
   9913     assert(monthsToMonth(Month.jan, Month.sep) == 8);
   9914     assert(monthsToMonth(Month.jan, Month.oct) == 9);
   9915     assert(monthsToMonth(Month.jan, Month.nov) == 10);
   9916     assert(monthsToMonth(Month.jan, Month.dec) == 11);
   9917 
   9918     assert(monthsToMonth(Month.may, Month.jan) == 8);
   9919     assert(monthsToMonth(Month.may, Month.feb) == 9);
   9920     assert(monthsToMonth(Month.may, Month.mar) == 10);
   9921     assert(monthsToMonth(Month.may, Month.apr) == 11);
   9922     assert(monthsToMonth(Month.may, Month.may) == 0);
   9923     assert(monthsToMonth(Month.may, Month.jun) == 1);
   9924     assert(monthsToMonth(Month.may, Month.jul) == 2);
   9925     assert(monthsToMonth(Month.may, Month.aug) == 3);
   9926     assert(monthsToMonth(Month.may, Month.sep) == 4);
   9927     assert(monthsToMonth(Month.may, Month.oct) == 5);
   9928     assert(monthsToMonth(Month.may, Month.nov) == 6);
   9929     assert(monthsToMonth(Month.may, Month.dec) == 7);
   9930 
   9931     assert(monthsToMonth(Month.oct, Month.jan) == 3);
   9932     assert(monthsToMonth(Month.oct, Month.feb) == 4);
   9933     assert(monthsToMonth(Month.oct, Month.mar) == 5);
   9934     assert(monthsToMonth(Month.oct, Month.apr) == 6);
   9935     assert(monthsToMonth(Month.oct, Month.may) == 7);
   9936     assert(monthsToMonth(Month.oct, Month.jun) == 8);
   9937     assert(monthsToMonth(Month.oct, Month.jul) == 9);
   9938     assert(monthsToMonth(Month.oct, Month.aug) == 10);
   9939     assert(monthsToMonth(Month.oct, Month.sep) == 11);
   9940     assert(monthsToMonth(Month.oct, Month.oct) == 0);
   9941     assert(monthsToMonth(Month.oct, Month.nov) == 1);
   9942     assert(monthsToMonth(Month.oct, Month.dec) == 2);
   9943 
   9944     assert(monthsToMonth(Month.dec, Month.jan) == 1);
   9945     assert(monthsToMonth(Month.dec, Month.feb) == 2);
   9946     assert(monthsToMonth(Month.dec, Month.mar) == 3);
   9947     assert(monthsToMonth(Month.dec, Month.apr) == 4);
   9948     assert(monthsToMonth(Month.dec, Month.may) == 5);
   9949     assert(monthsToMonth(Month.dec, Month.jun) == 6);
   9950     assert(monthsToMonth(Month.dec, Month.jul) == 7);
   9951     assert(monthsToMonth(Month.dec, Month.aug) == 8);
   9952     assert(monthsToMonth(Month.dec, Month.sep) == 9);
   9953     assert(monthsToMonth(Month.dec, Month.oct) == 10);
   9954     assert(monthsToMonth(Month.dec, Month.nov) == 11);
   9955     assert(monthsToMonth(Month.dec, Month.dec) == 0);
   9956 }
   9957 
   9958 
   9959 /++
   9960     Whether the given Gregorian Year is a leap year.
   9961 
   9962     Params:
   9963         year = The year to to be tested.
   9964  +/
   9965 bool yearIsLeapYear(int year) @safe pure nothrow @nogc
   9966 {
   9967     if (year % 400 == 0)
   9968         return true;
   9969     if (year % 100 == 0)
   9970         return false;
   9971     return year % 4 == 0;
   9972 }
   9973 
   9974 ///
   9975 @safe unittest
   9976 {
   9977     foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
   9978     {
   9979         assert(!yearIsLeapYear(year));
   9980         assert(!yearIsLeapYear(-year));
   9981     }
   9982 
   9983     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
   9984     {
   9985         assert(yearIsLeapYear(year));
   9986         assert(yearIsLeapYear(-year));
   9987     }
   9988 }
   9989 
   9990 @safe unittest
   9991 {
   9992     import std.format : format;
   9993     foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
   9994                     2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
   9995     {
   9996         assert(!yearIsLeapYear(year), format("year: %s.", year));
   9997         assert(!yearIsLeapYear(-year), format("year: %s.", year));
   9998     }
   9999 
   10000     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
   10001     {
   10002         assert(yearIsLeapYear(year), format("year: %s.", year));
   10003         assert(yearIsLeapYear(-year), format("year: %s.", year));
   10004     }
   10005 }
   10006 
   10007 
   10008 /++
   10009     Whether the given type defines all of the necessary functions for it to
   10010     function as a time point.
   10011 
   10012     1. `T` must define a static property named `min` which is the smallest
   10013        value of `T` as `Unqual!T`.
   10014 
   10015     2. `T` must define a static property named `max` which is the largest
   10016        value of `T` as `Unqual!T`.
   10017 
   10018     3. `T` must define an `opBinary` for addition and subtraction that
   10019        accepts $(REF Duration, core,time) and returns `Unqual!T`.
   10020 
   10021     4. `T` must define an `opOpAssign` for addition and subtraction that
   10022        accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
   10023 
   10024     5. `T` must define a `opBinary` for subtraction which accepts `T`
   10025        and returns $(REF Duration, core,time).
   10026   +/
   10027 template isTimePoint(T)
   10028 {
   10029     import core.time : Duration;
   10030     import std.traits : FunctionAttribute, functionAttributes, Unqual;
   10031 
   10032     enum isTimePoint = hasMin &&
   10033                        hasMax &&
   10034                        hasOverloadedOpBinaryWithDuration &&
   10035                        hasOverloadedOpAssignWithDuration &&
   10036                        hasOverloadedOpBinaryWithSelf &&
   10037                        !is(U == Duration);
   10038 
   10039 private:
   10040 
   10041     alias U = Unqual!T;
   10042 
   10043     enum hasMin = __traits(hasMember, T, "min") &&
   10044                   is(typeof(T.min) == U) &&
   10045                   is(typeof({static assert(__traits(isStaticFunction, T.min));}));
   10046 
   10047     enum hasMax = __traits(hasMember, T, "max") &&
   10048                   is(typeof(T.max) == U) &&
   10049                   is(typeof({static assert(__traits(isStaticFunction, T.max));}));
   10050 
   10051     enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
   10052                                              is(typeof(T.init - Duration.init) == U);
   10053 
   10054     enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
   10055                                              is(typeof(U.init -= Duration.init) == U) &&
   10056                                              is(typeof(
   10057                                              {
   10058                                                  alias add = U.opOpAssign!"+";
   10059                                                  alias sub = U.opOpAssign!"-";
   10060                                                  alias FA = FunctionAttribute;
   10061                                                  static assert((functionAttributes!add & FA.ref_) != 0);
   10062                                                  static assert((functionAttributes!sub & FA.ref_) != 0);
   10063                                              }));
   10064 
   10065     enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
   10066 }
   10067 
   10068 ///
   10069 @safe unittest
   10070 {
   10071     import core.time : Duration;
   10072     import std.datetime.interval : Interval;
   10073     import std.datetime.systime : SysTime;
   10074 
   10075     static assert(isTimePoint!Date);
   10076     static assert(isTimePoint!DateTime);
   10077     static assert(isTimePoint!SysTime);
   10078     static assert(isTimePoint!TimeOfDay);
   10079 
   10080     static assert(!isTimePoint!int);
   10081     static assert(!isTimePoint!Duration);
   10082     static assert(!isTimePoint!(Interval!SysTime));
   10083 }
   10084 
   10085 @safe unittest
   10086 {
   10087     import core.time;
   10088     import std.datetime.interval;
   10089     import std.datetime.systime;
   10090     import std.meta : AliasSeq;
   10091 
   10092     static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
   10093     {
   10094         static assert(isTimePoint!(const TP), TP.stringof);
   10095         static assert(isTimePoint!(immutable TP), TP.stringof);
   10096     }
   10097 
   10098     static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
   10099         static assert(!isTimePoint!T, T.stringof);
   10100 }
   10101 
   10102 
   10103 /++
   10104     Whether all of the given strings are valid units of time.
   10105 
   10106     `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
   10107     can handle precision greater than hnsecs, and the few functions in core.time
   10108     which deal with "nsecs" deal with it explicitly.
   10109   +/
   10110 bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
   10111 {
   10112     import std.algorithm.searching : canFind;
   10113     foreach (str; units)
   10114     {
   10115         if (!canFind(timeStrings[], str))
   10116             return false;
   10117     }
   10118     return true;
   10119 }
   10120 
   10121 ///
   10122 @safe @nogc nothrow unittest
   10123 {
   10124     assert(validTimeUnits("msecs", "seconds", "minutes"));
   10125     assert(validTimeUnits("days", "weeks", "months"));
   10126     assert(!validTimeUnits("ms", "seconds", "minutes"));
   10127 }
   10128 
   10129 
   10130 /++
   10131     Compares two time unit strings. `"years"` are the largest units and
   10132     `"hnsecs"` are the smallest.
   10133 
   10134     Returns:
   10135         $(BOOKTABLE,
   10136         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
   10137         $(TR $(TD this == rhs) $(TD 0))
   10138         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
   10139         )
   10140 
   10141     Throws:
   10142         $(LREF DateTimeException) if either of the given strings is not a valid
   10143         time unit string.
   10144  +/
   10145 int cmpTimeUnits(string lhs, string rhs) @safe pure
   10146 {
   10147     import std.algorithm.searching : countUntil;
   10148     import std.exception : enforce;
   10149     import std.format : format;
   10150 
   10151     immutable indexOfLHS = countUntil(timeStrings, lhs);
   10152     immutable indexOfRHS = countUntil(timeStrings, rhs);
   10153 
   10154     enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
   10155     enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
   10156 
   10157     if (indexOfLHS < indexOfRHS)
   10158         return -1;
   10159     if (indexOfLHS > indexOfRHS)
   10160         return 1;
   10161 
   10162     return 0;
   10163 }
   10164 
   10165 ///
   10166 @safe pure unittest
   10167 {
   10168     import std.exception : assertThrown;
   10169 
   10170     assert(cmpTimeUnits("hours", "hours") == 0);
   10171     assert(cmpTimeUnits("hours", "weeks") < 0);
   10172     assert(cmpTimeUnits("months", "seconds") > 0);
   10173 
   10174     assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
   10175 }
   10176 
   10177 @safe unittest
   10178 {
   10179     foreach (i, outerUnits; timeStrings)
   10180     {
   10181         assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
   10182 
   10183         // For some reason, $ won't compile.
   10184         foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
   10185             assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
   10186     }
   10187 
   10188     foreach (i, outerUnits; timeStrings)
   10189     {
   10190         foreach (innerUnits; timeStrings[0 .. i])
   10191             assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
   10192     }
   10193 }
   10194 
   10195 
   10196 /++
   10197     Compares two time unit strings at compile time. `"years"` are the largest
   10198     units and `"hnsecs"` are the smallest.
   10199 
   10200     This template is used instead of `cmpTimeUnits` because exceptions
   10201     can't be thrown at compile time and `cmpTimeUnits` must enforce that
   10202     the strings it's given are valid time unit strings. This template uses a
   10203     template constraint instead.
   10204 
   10205     Returns:
   10206         $(BOOKTABLE,
   10207         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
   10208         $(TR $(TD this == rhs) $(TD 0))
   10209         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
   10210         )
   10211  +/
   10212 template CmpTimeUnits(string lhs, string rhs)
   10213 if (validTimeUnits(lhs, rhs))
   10214 {
   10215     enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
   10216 }
   10217 
   10218 ///
   10219 @safe pure unittest
   10220 {
   10221     static assert(CmpTimeUnits!("years", "weeks") > 0);
   10222     static assert(CmpTimeUnits!("days", "days") == 0);
   10223     static assert(CmpTimeUnits!("seconds", "hours") < 0);
   10224 }
   10225 
   10226 // Helper function for CmpTimeUnits.
   10227 private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
   10228 {
   10229     import std.algorithm.searching : countUntil;
   10230     auto tstrings = timeStrings;
   10231     immutable indexOfLHS = countUntil(tstrings, lhs);
   10232     immutable indexOfRHS = countUntil(tstrings, rhs);
   10233 
   10234     if (indexOfLHS < indexOfRHS)
   10235         return -1;
   10236     if (indexOfLHS > indexOfRHS)
   10237         return 1;
   10238 
   10239     return 0;
   10240 }
   10241 
   10242 @safe unittest
   10243 {
   10244     static foreach (i; 0 .. timeStrings.length)
   10245     {
   10246         static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
   10247 
   10248         static foreach (next; timeStrings[i + 1 .. $])
   10249             static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
   10250 
   10251         static foreach (prev; timeStrings[0 .. i])
   10252             static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
   10253     }
   10254 }
   10255 
   10256 
   10257 package:
   10258 
   10259 
   10260 /+
   10261     Array of the short (three letter) names of each month.
   10262   +/
   10263 immutable string[12] _monthNames = ["Jan",
   10264                                     "Feb",
   10265                                     "Mar",
   10266                                     "Apr",
   10267                                     "May",
   10268                                     "Jun",
   10269                                     "Jul",
   10270                                     "Aug",
   10271                                     "Sep",
   10272                                     "Oct",
   10273                                     "Nov",
   10274                                     "Dec"];
   10275 
   10276 /+
   10277     The maximum valid Day in the given month in the given year.
   10278 
   10279     Params:
   10280         year  = The year to get the day for.
   10281         month = The month of the Gregorian Calendar to get the day for.
   10282  +/
   10283 ubyte maxDay(int year, int month) @safe pure nothrow @nogc
   10284 in
   10285 {
   10286     assert(valid!"months"(month));
   10287 }
   10288 do
   10289 {
   10290     switch (month)
   10291     {
   10292         case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
   10293             return 31;
   10294         case Month.feb:
   10295             return yearIsLeapYear(year) ? 29 : 28;
   10296         case Month.apr, Month.jun, Month.sep, Month.nov:
   10297             return 30;
   10298         default:
   10299             assert(0, "Invalid month.");
   10300     }
   10301 }
   10302 
   10303 @safe unittest
   10304 {
   10305     // Test A.D.
   10306     assert(maxDay(1999, 1) == 31);
   10307     assert(maxDay(1999, 2) == 28);
   10308     assert(maxDay(1999, 3) == 31);
   10309     assert(maxDay(1999, 4) == 30);
   10310     assert(maxDay(1999, 5) == 31);
   10311     assert(maxDay(1999, 6) == 30);
   10312     assert(maxDay(1999, 7) == 31);
   10313     assert(maxDay(1999, 8) == 31);
   10314     assert(maxDay(1999, 9) == 30);
   10315     assert(maxDay(1999, 10) == 31);
   10316     assert(maxDay(1999, 11) == 30);
   10317     assert(maxDay(1999, 12) == 31);
   10318 
   10319     assert(maxDay(2000, 1) == 31);
   10320     assert(maxDay(2000, 2) == 29);
   10321     assert(maxDay(2000, 3) == 31);
   10322     assert(maxDay(2000, 4) == 30);
   10323     assert(maxDay(2000, 5) == 31);
   10324     assert(maxDay(2000, 6) == 30);
   10325     assert(maxDay(2000, 7) == 31);
   10326     assert(maxDay(2000, 8) == 31);
   10327     assert(maxDay(2000, 9) == 30);
   10328     assert(maxDay(2000, 10) == 31);
   10329     assert(maxDay(2000, 11) == 30);
   10330     assert(maxDay(2000, 12) == 31);
   10331 
   10332     // Test B.C.
   10333     assert(maxDay(-1999, 1) == 31);
   10334     assert(maxDay(-1999, 2) == 28);
   10335     assert(maxDay(-1999, 3) == 31);
   10336     assert(maxDay(-1999, 4) == 30);
   10337     assert(maxDay(-1999, 5) == 31);
   10338     assert(maxDay(-1999, 6) == 30);
   10339     assert(maxDay(-1999, 7) == 31);
   10340     assert(maxDay(-1999, 8) == 31);
   10341     assert(maxDay(-1999, 9) == 30);
   10342     assert(maxDay(-1999, 10) == 31);
   10343     assert(maxDay(-1999, 11) == 30);
   10344     assert(maxDay(-1999, 12) == 31);
   10345 
   10346     assert(maxDay(-2000, 1) == 31);
   10347     assert(maxDay(-2000, 2) == 29);
   10348     assert(maxDay(-2000, 3) == 31);
   10349     assert(maxDay(-2000, 4) == 30);
   10350     assert(maxDay(-2000, 5) == 31);
   10351     assert(maxDay(-2000, 6) == 30);
   10352     assert(maxDay(-2000, 7) == 31);
   10353     assert(maxDay(-2000, 8) == 31);
   10354     assert(maxDay(-2000, 9) == 30);
   10355     assert(maxDay(-2000, 10) == 31);
   10356     assert(maxDay(-2000, 11) == 30);
   10357     assert(maxDay(-2000, 12) == 31);
   10358 }
   10359 
   10360 /+
   10361     Splits out a particular unit from hnsecs and gives the value for that
   10362     unit and the remaining hnsecs. It really shouldn't be used unless unless
   10363     all units larger than the given units have already been split out.
   10364 
   10365     Params:
   10366         units  = The units to split out.
   10367         hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
   10368                  after splitting out the given units.
   10369 
   10370     Returns:
   10371         The number of the given units from converting hnsecs to those units.
   10372   +/
   10373 long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
   10374 if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
   10375 {
   10376     import core.time : convert;
   10377     immutable value = convert!("hnsecs", units)(hnsecs);
   10378     hnsecs -= convert!(units, "hnsecs")(value);
   10379     return value;
   10380 }
   10381 
   10382 @safe unittest
   10383 {
   10384     auto hnsecs = 2595000000007L;
   10385     immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
   10386     assert(days == 3);
   10387     assert(hnsecs == 3000000007);
   10388 
   10389     immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
   10390     assert(minutes == 5);
   10391     assert(hnsecs == 7);
   10392 }
   10393 
   10394 
   10395 /+
   10396     Returns the day of the week for the given day of the Gregorian Calendar.
   10397 
   10398     Params:
   10399         day = The day of the Gregorian Calendar for which to get the day of
   10400               the week.
   10401   +/
   10402 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
   10403 {
   10404     // January 1st, 1 A.D. was a Monday
   10405     if (day >= 0)
   10406         return cast(DayOfWeek)(day % 7);
   10407     else
   10408     {
   10409         immutable dow = cast(DayOfWeek)((day % 7) + 7);
   10410 
   10411         if (dow == 7)
   10412             return DayOfWeek.sun;
   10413         else
   10414             return dow;
   10415     }
   10416 }
   10417 
   10418 @safe unittest
   10419 {
   10420     import std.datetime.systime : SysTime;
   10421 
   10422     // Test A.D.
   10423     assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
   10424     assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
   10425     assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
   10426     assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
   10427     assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
   10428     assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
   10429     assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
   10430     assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
   10431     assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
   10432     assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
   10433     assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
   10434     assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
   10435     assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
   10436     assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
   10437     assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
   10438     assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
   10439     assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
   10440     assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
   10441     assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
   10442     assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
   10443     assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
   10444     assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
   10445 
   10446     // Test B.C.
   10447     assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
   10448     assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
   10449     assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
   10450     assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
   10451     assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
   10452     assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
   10453     assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
   10454     assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
   10455     assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
   10456 }
   10457 
   10458 
   10459 private:
   10460 
   10461 enum daysInYear     = 365;  // The number of days in a non-leap year.
   10462 enum daysInLeapYear = 366;  // The numbef or days in a leap year.
   10463 enum daysIn4Years   = daysInYear * 3 + daysInLeapYear;  // Number of days in 4 years.
   10464 enum daysIn100Years = daysIn4Years * 25 - 1;  // The number of days in 100 years.
   10465 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
   10466 
   10467 /+
   10468     Array of integers representing the last days of each month in a year.
   10469   +/
   10470 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
   10471 
   10472 /+
   10473     Array of integers representing the last days of each month in a leap year.
   10474   +/
   10475 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
   10476 
   10477 
   10478 /+
   10479     Returns the string representation of the given month.
   10480   +/
   10481 string monthToString(Month month) @safe pure
   10482 {
   10483     import std.format : format;
   10484     assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
   10485     return _monthNames[month - Month.jan];
   10486 }
   10487 
   10488 @safe unittest
   10489 {
   10490     assert(monthToString(Month.jan) == "Jan");
   10491     assert(monthToString(Month.feb) == "Feb");
   10492     assert(monthToString(Month.mar) == "Mar");
   10493     assert(monthToString(Month.apr) == "Apr");
   10494     assert(monthToString(Month.may) == "May");
   10495     assert(monthToString(Month.jun) == "Jun");
   10496     assert(monthToString(Month.jul) == "Jul");
   10497     assert(monthToString(Month.aug) == "Aug");
   10498     assert(monthToString(Month.sep) == "Sep");
   10499     assert(monthToString(Month.oct) == "Oct");
   10500     assert(monthToString(Month.nov) == "Nov");
   10501     assert(monthToString(Month.dec) == "Dec");
   10502 }
   10503 
   10504 
   10505 /+
   10506     Returns the Month corresponding to the given string.
   10507 
   10508     Params:
   10509         monthStr = The string representation of the month to get the Month for.
   10510 
   10511     Throws:
   10512         $(REF DateTimeException,std,datetime,date) if the given month is not a
   10513         valid month string.
   10514   +/
   10515 Month monthFromString(T)(T monthStr) @safe pure
   10516 if (isSomeString!T)
   10517 {
   10518     import std.format : format;
   10519     switch (monthStr)
   10520     {
   10521         case "Jan":
   10522             return Month.jan;
   10523         case "Feb":
   10524             return Month.feb;
   10525         case "Mar":
   10526             return Month.mar;
   10527         case "Apr":
   10528             return Month.apr;
   10529         case "May":
   10530             return Month.may;
   10531         case "Jun":
   10532             return Month.jun;
   10533         case "Jul":
   10534             return Month.jul;
   10535         case "Aug":
   10536             return Month.aug;
   10537         case "Sep":
   10538             return Month.sep;
   10539         case "Oct":
   10540             return Month.oct;
   10541         case "Nov":
   10542             return Month.nov;
   10543         case "Dec":
   10544             return Month.dec;
   10545         default:
   10546             throw new DateTimeException(format!"Invalid month %s"(monthStr));
   10547     }
   10548 }
   10549 
   10550 @safe unittest
   10551 {
   10552     import std.conv : to;
   10553     import std.traits : EnumMembers;
   10554     foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
   10555                       "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
   10556     {
   10557         assertThrown!DateTimeException(monthFromString(badStr), badStr);
   10558     }
   10559 
   10560     foreach (month; EnumMembers!Month)
   10561     {
   10562         assert(monthFromString(monthToString(month)) == month, month.to!string);
   10563     }
   10564 }
   10565 
   10566 
   10567 // NOTE: all the non-simple array literals are wrapped in functions, because
   10568 // otherwise importing causes re-evaluation of the static initializers using
   10569 // CTFE with unittests enabled
   10570 version (StdUnittest)
   10571 {
   10572 private @safe:
   10573     // All of these helper arrays are sorted in ascending order.
   10574     auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
   10575     auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
   10576 
   10577     // I'd use a Tuple, but I get forward reference errors if I try.
   10578     struct MonthDay
   10579     {
   10580         Month month;
   10581         short day;
   10582 
   10583         this(int m, short d)
   10584         {
   10585             month = cast(Month) m;
   10586             day = d;
   10587         }
   10588     }
   10589 
   10590     MonthDay[] testMonthDays()
   10591     {
   10592         static MonthDay[] result = [MonthDay(1, 1),
   10593                                 MonthDay(1, 2),
   10594                                 MonthDay(3, 17),
   10595                                 MonthDay(7, 4),
   10596                                 MonthDay(10, 27),
   10597                                 MonthDay(12, 30),
   10598                                 MonthDay(12, 31)];
   10599         return result;
   10600     }
   10601 
   10602     auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
   10603 
   10604     TimeOfDay[] testTODs()
   10605     {
   10606         static result = [TimeOfDay(0, 0, 0),
   10607                      TimeOfDay(0, 0, 1),
   10608                      TimeOfDay(0, 1, 0),
   10609                      TimeOfDay(1, 0, 0),
   10610                      TimeOfDay(13, 13, 13),
   10611                      TimeOfDay(23, 59, 59)];
   10612         return result;
   10613     }
   10614 
   10615     auto testHours = [0, 1, 12, 22, 23];
   10616     auto testMinSecs = [0, 1, 30, 58, 59];
   10617 
   10618     // Throwing exceptions is incredibly expensive, so we want to use a smaller
   10619     // set of values for tests using assertThrown.
   10620     TimeOfDay[] testTODsThrown()
   10621     {
   10622        static result = [TimeOfDay(0, 0, 0),
   10623                            TimeOfDay(13, 13, 13),
   10624                            TimeOfDay(23, 59, 59)];
   10625        return result;
   10626     }
   10627 
   10628     Date[] testDatesBC;
   10629     Date[] testDatesAD;
   10630 
   10631     DateTime[] testDateTimesBC;
   10632     DateTime[] testDateTimesAD;
   10633 
   10634     // I'd use a Tuple, but I get forward reference errors if I try.
   10635     struct GregDay { int day; Date date; }
   10636     GregDay[] testGregDaysBC()
   10637     {
   10638        static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
   10639                            GregDay(-735_233, Date(-2012, 1, 1)),
   10640                            GregDay(-735_202, Date(-2012, 2, 1)),
   10641                            GregDay(-735_175, Date(-2012, 2, 28)),
   10642                            GregDay(-735_174, Date(-2012, 2, 29)),
   10643                            GregDay(-735_173, Date(-2012, 3, 1)),
   10644                            GregDay(-734_502, Date(-2010, 1, 1)),
   10645                            GregDay(-734_472, Date(-2010, 1, 31)),
   10646                            GregDay(-734_471, Date(-2010, 2, 1)),
   10647                            GregDay(-734_444, Date(-2010, 2, 28)),
   10648                            GregDay(-734_443, Date(-2010, 3, 1)),
   10649                            GregDay(-734_413, Date(-2010, 3, 31)),
   10650                            GregDay(-734_412, Date(-2010, 4, 1)),
   10651                            GregDay(-734_383, Date(-2010, 4, 30)),
   10652                            GregDay(-734_382, Date(-2010, 5, 1)),
   10653                            GregDay(-734_352, Date(-2010, 5, 31)),
   10654                            GregDay(-734_351, Date(-2010, 6, 1)),
   10655                            GregDay(-734_322, Date(-2010, 6, 30)),
   10656                            GregDay(-734_321, Date(-2010, 7, 1)),
   10657                            GregDay(-734_291, Date(-2010, 7, 31)),
   10658                            GregDay(-734_290, Date(-2010, 8, 1)),
   10659                            GregDay(-734_260, Date(-2010, 8, 31)),
   10660                            GregDay(-734_259, Date(-2010, 9, 1)),
   10661                            GregDay(-734_230, Date(-2010, 9, 30)),
   10662                            GregDay(-734_229, Date(-2010, 10, 1)),
   10663                            GregDay(-734_199, Date(-2010, 10, 31)),
   10664                            GregDay(-734_198, Date(-2010, 11, 1)),
   10665                            GregDay(-734_169, Date(-2010, 11, 30)),
   10666                            GregDay(-734_168, Date(-2010, 12, 1)),
   10667                            GregDay(-734_139, Date(-2010, 12, 30)),
   10668                            GregDay(-734_138, Date(-2010, 12, 31)),
   10669                            GregDay(-731_215, Date(-2001, 1, 1)),
   10670                            GregDay(-730_850, Date(-2000, 1, 1)),
   10671                            GregDay(-730_849, Date(-2000, 1, 2)),
   10672                            GregDay(-730_486, Date(-2000, 12, 30)),
   10673                            GregDay(-730_485, Date(-2000, 12, 31)),
   10674                            GregDay(-730_484, Date(-1999, 1, 1)),
   10675                            GregDay(-694_690, Date(-1901, 1, 1)),
   10676                            GregDay(-694_325, Date(-1900, 1, 1)),
   10677                            GregDay(-585_118, Date(-1601, 1, 1)),
   10678                            GregDay(-584_753, Date(-1600, 1, 1)),
   10679                            GregDay(-584_388, Date(-1600, 12, 31)),
   10680                            GregDay(-584_387, Date(-1599, 1, 1)),
   10681                            GregDay(-365_972, Date(-1001, 1, 1)),
   10682                            GregDay(-365_607, Date(-1000, 1, 1)),
   10683                            GregDay(-183_351, Date(-501, 1, 1)),
   10684                            GregDay(-182_986, Date(-500, 1, 1)),
   10685                            GregDay(-182_621, Date(-499, 1, 1)),
   10686                            GregDay(-146_827, Date(-401, 1, 1)),
   10687                            GregDay(-146_462, Date(-400, 1, 1)),
   10688                            GregDay(-146_097, Date(-400, 12, 31)),
   10689                            GregDay(-110_302, Date(-301, 1, 1)),
   10690                            GregDay(-109_937, Date(-300, 1, 1)),
   10691                            GregDay(-73_778, Date(-201, 1, 1)),
   10692                            GregDay(-73_413, Date(-200, 1, 1)),
   10693                            GregDay(-38_715, Date(-105, 1, 1)),
   10694                            GregDay(-37_254, Date(-101, 1, 1)),
   10695                            GregDay(-36_889, Date(-100, 1, 1)),
   10696                            GregDay(-36_524, Date(-99, 1, 1)),
   10697                            GregDay(-36_160, Date(-99, 12, 31)),
   10698                            GregDay(-35_794, Date(-97, 1, 1)),
   10699                            GregDay(-18_627, Date(-50, 1, 1)),
   10700                            GregDay(-18_262, Date(-49, 1, 1)),
   10701                            GregDay(-3652, Date(-9, 1, 1)),
   10702                            GregDay(-2191, Date(-5, 1, 1)),
   10703                            GregDay(-1827, Date(-5, 12, 31)),
   10704                            GregDay(-1826, Date(-4, 1, 1)),
   10705                            GregDay(-1825, Date(-4, 1, 2)),
   10706                            GregDay(-1462, Date(-4, 12, 30)),
   10707                            GregDay(-1461, Date(-4, 12, 31)),
   10708                            GregDay(-1460, Date(-3, 1, 1)),
   10709                            GregDay(-1096, Date(-3, 12, 31)),
   10710                            GregDay(-1095, Date(-2, 1, 1)),
   10711                            GregDay(-731, Date(-2, 12, 31)),
   10712                            GregDay(-730, Date(-1, 1, 1)),
   10713                            GregDay(-367, Date(-1, 12, 30)),
   10714                            GregDay(-366, Date(-1, 12, 31)),
   10715                            GregDay(-365, Date(0, 1, 1)),
   10716                            GregDay(-31, Date(0, 11, 30)),
   10717                            GregDay(-30, Date(0, 12, 1)),
   10718                            GregDay(-1, Date(0, 12, 30)),
   10719                            GregDay(0, Date(0, 12, 31))];
   10720        return result;
   10721     }
   10722 
   10723     GregDay[] testGregDaysAD()
   10724     {
   10725        static result = [GregDay(1, Date(1, 1, 1)),
   10726                            GregDay(2, Date(1, 1, 2)),
   10727                            GregDay(32, Date(1, 2, 1)),
   10728                            GregDay(365, Date(1, 12, 31)),
   10729                            GregDay(366, Date(2, 1, 1)),
   10730                            GregDay(731, Date(3, 1, 1)),
   10731                            GregDay(1096, Date(4, 1, 1)),
   10732                            GregDay(1097, Date(4, 1, 2)),
   10733                            GregDay(1460, Date(4, 12, 30)),
   10734                            GregDay(1461, Date(4, 12, 31)),
   10735                            GregDay(1462, Date(5, 1, 1)),
   10736                            GregDay(17_898, Date(50, 1, 1)),
   10737                            GregDay(35_065, Date(97, 1, 1)),
   10738                            GregDay(36_160, Date(100, 1, 1)),
   10739                            GregDay(36_525, Date(101, 1, 1)),
   10740                            GregDay(37_986, Date(105, 1, 1)),
   10741                            GregDay(72_684, Date(200, 1, 1)),
   10742                            GregDay(73_049, Date(201, 1, 1)),
   10743                            GregDay(109_208, Date(300, 1, 1)),
   10744                            GregDay(109_573, Date(301, 1, 1)),
   10745                            GregDay(145_732, Date(400, 1, 1)),
   10746                            GregDay(146_098, Date(401, 1, 1)),
   10747                            GregDay(182_257, Date(500, 1, 1)),
   10748                            GregDay(182_622, Date(501, 1, 1)),
   10749                            GregDay(364_878, Date(1000, 1, 1)),
   10750                            GregDay(365_243, Date(1001, 1, 1)),
   10751                            GregDay(584_023, Date(1600, 1, 1)),
   10752                            GregDay(584_389, Date(1601, 1, 1)),
   10753                            GregDay(693_596, Date(1900, 1, 1)),
   10754                            GregDay(693_961, Date(1901, 1, 1)),
   10755                            GregDay(729_755, Date(1999, 1, 1)),
   10756                            GregDay(730_120, Date(2000, 1, 1)),
   10757                            GregDay(730_121, Date(2000, 1, 2)),
   10758                            GregDay(730_484, Date(2000, 12, 30)),
   10759                            GregDay(730_485, Date(2000, 12, 31)),
   10760                            GregDay(730_486, Date(2001, 1, 1)),
   10761                            GregDay(733_773, Date(2010, 1, 1)),
   10762                            GregDay(733_774, Date(2010, 1, 2)),
   10763                            GregDay(733_803, Date(2010, 1, 31)),
   10764                            GregDay(733_804, Date(2010, 2, 1)),
   10765                            GregDay(733_831, Date(2010, 2, 28)),
   10766                            GregDay(733_832, Date(2010, 3, 1)),
   10767                            GregDay(733_862, Date(2010, 3, 31)),
   10768                            GregDay(733_863, Date(2010, 4, 1)),
   10769                            GregDay(733_892, Date(2010, 4, 30)),
   10770                            GregDay(733_893, Date(2010, 5, 1)),
   10771                            GregDay(733_923, Date(2010, 5, 31)),
   10772                            GregDay(733_924, Date(2010, 6, 1)),
   10773                            GregDay(733_953, Date(2010, 6, 30)),
   10774                            GregDay(733_954, Date(2010, 7, 1)),
   10775                            GregDay(733_984, Date(2010, 7, 31)),
   10776                            GregDay(733_985, Date(2010, 8, 1)),
   10777                            GregDay(734_015, Date(2010, 8, 31)),
   10778                            GregDay(734_016, Date(2010, 9, 1)),
   10779                            GregDay(734_045, Date(2010, 9, 30)),
   10780                            GregDay(734_046, Date(2010, 10, 1)),
   10781                            GregDay(734_076, Date(2010, 10, 31)),
   10782                            GregDay(734_077, Date(2010, 11, 1)),
   10783                            GregDay(734_106, Date(2010, 11, 30)),
   10784                            GregDay(734_107, Date(2010, 12, 1)),
   10785                            GregDay(734_136, Date(2010, 12, 30)),
   10786                            GregDay(734_137, Date(2010, 12, 31)),
   10787                            GregDay(734_503, Date(2012, 1, 1)),
   10788                            GregDay(734_534, Date(2012, 2, 1)),
   10789                            GregDay(734_561, Date(2012, 2, 28)),
   10790                            GregDay(734_562, Date(2012, 2, 29)),
   10791                            GregDay(734_563, Date(2012, 3, 1)),
   10792                            GregDay(734_858, Date(2012, 12, 21))];
   10793        return result;
   10794     }
   10795 
   10796     // I'd use a Tuple, but I get forward reference errors if I try.
   10797     struct DayOfYear { int day; MonthDay md; }
   10798     DayOfYear[] testDaysOfYear()
   10799     {
   10800        static result = [DayOfYear(1, MonthDay(1, 1)),
   10801                            DayOfYear(2, MonthDay(1, 2)),
   10802                            DayOfYear(3, MonthDay(1, 3)),
   10803                            DayOfYear(31, MonthDay(1, 31)),
   10804                            DayOfYear(32, MonthDay(2, 1)),
   10805                            DayOfYear(59, MonthDay(2, 28)),
   10806                            DayOfYear(60, MonthDay(3, 1)),
   10807                            DayOfYear(90, MonthDay(3, 31)),
   10808                            DayOfYear(91, MonthDay(4, 1)),
   10809                            DayOfYear(120, MonthDay(4, 30)),
   10810                            DayOfYear(121, MonthDay(5, 1)),
   10811                            DayOfYear(151, MonthDay(5, 31)),
   10812                            DayOfYear(152, MonthDay(6, 1)),
   10813                            DayOfYear(181, MonthDay(6, 30)),
   10814                            DayOfYear(182, MonthDay(7, 1)),
   10815                            DayOfYear(212, MonthDay(7, 31)),
   10816                            DayOfYear(213, MonthDay(8, 1)),
   10817                            DayOfYear(243, MonthDay(8, 31)),
   10818                            DayOfYear(244, MonthDay(9, 1)),
   10819                            DayOfYear(273, MonthDay(9, 30)),
   10820                            DayOfYear(274, MonthDay(10, 1)),
   10821                            DayOfYear(304, MonthDay(10, 31)),
   10822                            DayOfYear(305, MonthDay(11, 1)),
   10823                            DayOfYear(334, MonthDay(11, 30)),
   10824                            DayOfYear(335, MonthDay(12, 1)),
   10825                            DayOfYear(363, MonthDay(12, 29)),
   10826                            DayOfYear(364, MonthDay(12, 30)),
   10827                            DayOfYear(365, MonthDay(12, 31))];
   10828        return result;
   10829     }
   10830 
   10831     DayOfYear[] testDaysOfLeapYear()
   10832     {
   10833        static result = [DayOfYear(1, MonthDay(1, 1)),
   10834                                DayOfYear(2, MonthDay(1, 2)),
   10835                                DayOfYear(3, MonthDay(1, 3)),
   10836                                DayOfYear(31, MonthDay(1, 31)),
   10837                                DayOfYear(32, MonthDay(2, 1)),
   10838                                DayOfYear(59, MonthDay(2, 28)),
   10839                                DayOfYear(60, MonthDay(2, 29)),
   10840                                DayOfYear(61, MonthDay(3, 1)),
   10841                                DayOfYear(91, MonthDay(3, 31)),
   10842                                DayOfYear(92, MonthDay(4, 1)),
   10843                                DayOfYear(121, MonthDay(4, 30)),
   10844                                DayOfYear(122, MonthDay(5, 1)),
   10845                                DayOfYear(152, MonthDay(5, 31)),
   10846                                DayOfYear(153, MonthDay(6, 1)),
   10847                                DayOfYear(182, MonthDay(6, 30)),
   10848                                DayOfYear(183, MonthDay(7, 1)),
   10849                                DayOfYear(213, MonthDay(7, 31)),
   10850                                DayOfYear(214, MonthDay(8, 1)),
   10851                                DayOfYear(244, MonthDay(8, 31)),
   10852                                DayOfYear(245, MonthDay(9, 1)),
   10853                                DayOfYear(274, MonthDay(9, 30)),
   10854                                DayOfYear(275, MonthDay(10, 1)),
   10855                                DayOfYear(305, MonthDay(10, 31)),
   10856                                DayOfYear(306, MonthDay(11, 1)),
   10857                                DayOfYear(335, MonthDay(11, 30)),
   10858                                DayOfYear(336, MonthDay(12, 1)),
   10859                                DayOfYear(364, MonthDay(12, 29)),
   10860                                DayOfYear(365, MonthDay(12, 30)),
   10861                                DayOfYear(366, MonthDay(12, 31))];
   10862        return result;
   10863     }
   10864 
   10865     void initializeTests()
   10866     {
   10867         foreach (year; testYearsBC)
   10868         {
   10869             foreach (md; testMonthDays)
   10870                 testDatesBC ~= Date(year, md.month, md.day);
   10871         }
   10872 
   10873         foreach (year; testYearsAD)
   10874         {
   10875             foreach (md; testMonthDays)
   10876                 testDatesAD ~= Date(year, md.month, md.day);
   10877         }
   10878 
   10879         foreach (dt; testDatesBC)
   10880         {
   10881             foreach (tod; testTODs)
   10882                 testDateTimesBC ~= DateTime(dt, tod);
   10883         }
   10884 
   10885         foreach (dt; testDatesAD)
   10886         {
   10887             foreach (tod; testTODs)
   10888                 testDateTimesAD ~= DateTime(dt, tod);
   10889         }
   10890     }
   10891 }
   10892