111497Sphk// Written in the D programming language
211497Sphk/++
311497Sphk
411497Sphk$(SCRIPT inhibitQuickIndex = 1;)
593151Sphk$(DIVC quickindex,
611497Sphk$(BOOKTABLE,
711497Sphk$(TR $(TH Category) $(TH Functions))
811497Sphk$(TR $(TD Main date types) $(TD
911497Sphk    $(LREF Date)
10108605Sphk    $(LREF DateTime)
11108605Sphk))
1250477Speter$(TR $(TD Other date types) $(TD
1311497Sphk    $(LREF Month)
14108605Sphk    $(LREF DayOfWeek)
15108605Sphk    $(LREF TimeOfDay)
1611497Sphk))
17103392Sphk$(TR $(TD Date checking) $(TD
1841594Sarchie    $(LREF valid)
19108605Sphk    $(LREF validTimeUnits)
2041594Sarchie    $(LREF yearIsLeapYear)
21108605Sphk    $(LREF isTimePoint)
22108605Sphk    $(LREF enforceValid)
23108605Sphk))
24108605Sphk$(TR $(TD Date conversion) $(TD
25103392Sphk    $(LREF daysToDayOfWeek)
26108605Sphk    $(LREF monthsToMonth)
27108605Sphk))
28108605Sphk$(TR $(TD Time units) $(TD
29108605Sphk    $(LREF cmpTimeUnits)
30108605Sphk    $(LREF timeStrings)
31108605Sphk))
32108605Sphk$(TR $(TD Other) $(TD
33108605Sphk    $(LREF AllowDayOverflow)
34108605Sphk    $(LREF DateTimeException)
35108605Sphk))
36108605Sphk))
37108605Sphk
38108605Sphk    License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
39108605Sphk    Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis)
40108605Sphk    Source:    $(PHOBOSSRC std/datetime/date.d)
41108605Sphk+/
42108605Sphkmodule std.datetime.date;
43108605Sphk
44108605Sphkimport core.time : TimeException;
45108605Sphkimport std.traits : isSomeString, Unqual;
46108605Sphkimport std.typecons : Flag;
47108605Sphkimport std.range.primitives : isOutputRange;
48108605Sphk
49108605Sphkversion (StdUnittest) import std.exception : assertThrown;
50108605Sphk
51108605Sphk@safe unittest
52108605Sphk{
53108605Sphk    initializeTests();
54108605Sphk}
55108605Sphk
56108605Sphk
57108605Sphk/++
58108605Sphk    Exception type used by std.datetime. It's an alias to
59108605Sphk    $(REF TimeException,core,time). Either can be caught without concern about
60108605Sphk    which module it came from.
61108605Sphk  +/
62108605Sphkalias DateTimeException = TimeException;
63108605Sphk
64108605Sphk
65108605Sphk/++
66108605Sphk    Represents the 12 months of the Gregorian year (January is 1).
67103392Sphk  +/
6841594Sarchieenum Month : ubyte
6941594Sarchie{
70141640Sphk    jan = 1, ///
71103392Sphk    feb,     ///
72141640Sphk    mar,     ///
73141640Sphk    apr,     ///
74141640Sphk    may,     ///
75141640Sphk    jun,     ///
76141640Sphk    jul,     ///
77141640Sphk    aug,     ///
78141640Sphk    sep,     ///
79141640Sphk    oct,     ///
80141640Sphk    nov,     ///
8111497Sphk    dec      ///
8211497Sphk}
8311497Sphk
8411497Sphk///
8511497Sphk@safe pure unittest
8611497Sphk{
8711497Sphk    assert(Date(2018, 10, 1).month == Month.oct);
88141640Sphk    assert(DateTime(1, 1, 1).month == Month.jan);
89141640Sphk}
90141640Sphk
91141640Sphk
9211497Sphk/++
93141640Sphk    Represents the 7 days of the Gregorian week (Sunday is 0).
94141640Sphk  +/
9511497Sphkenum DayOfWeek : ubyte
9611497Sphk{
9711497Sphk    sun = 0, ///
9811497Sphk    mon,     ///
9911497Sphk    tue,     ///
10011497Sphk    wed,     ///
10111497Sphk    thu,     ///
10211497Sphk    fri,     ///
103141640Sphk    sat      ///
10411497Sphk}
10511497Sphk
10611497Sphk///
10712814Sphk@safe pure unittest
10812814Sphk{
10911497Sphk    assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
11012814Sphk    assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
111141640Sphk}
11211497Sphk
11311497Sphk/++
11411497Sphk    In some date calculations, adding months or years can cause the date to fall
11511497Sphk    on a day of the month which is not valid (e.g. February 29th 2001 or
11611497Sphk    June 31st 2000). If overflow is allowed (as is the default), then the month
11711497Sphk    will be incremented accordingly (so, February 29th 2001 would become
11811497Sphk    March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
11911497Sphk    is not allowed, then the day will be adjusted to the last valid day in that
12011497Sphk    month (so, February 29th 2001 would become February 28th 2001 and
121108605Sphk    June 31st 2000 would become June 30th 2000).
12211497Sphk
12311497Sphk    AllowDayOverflow only applies to calculations involving months or years.
12411497Sphk
12511497Sphk    If set to `AllowDayOverflow.no`, then day overflow is not allowed.
126141640Sphk
12712870Sphk    Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
12811497Sphk    allowed.
129141640Sphk  +/
13012870Sphkalias AllowDayOverflow = Flag!"allowDayOverflow";
13111497Sphk
132141640Sphk
13312870Sphk/++
13412870Sphk    Array of the strings representing time units, starting with the smallest
135141640Sphk    unit and going to the largest. It does not include `"nsecs"`.
136141640Sphk
13712870Sphk    Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
13812870Sphk    `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
13912870Sphk    `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
14012870Sphk    `"years"`
14112870Sphk  +/
14212870Sphkimmutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
14312870Sphk                                  "hours", "days", "weeks", "months", "years"];
14412870Sphk
14512870Sphk
14612870Sphk/++
14712870Sphk    Combines the $(REF Date,std,datetime,date) and
14812870Sphk    $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
14911497Sphk    both the date and the time. It is optimized for calendar-based operations
15011497Sphk    and has no concept of time zone. For an object which is optimized for time
15112870Sphk    operations based on the system time, use
15212870Sphk    $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
15312875Sbde    a concept of time zone and has much higher precision (hnsecs). `DateTime`
15412875Sbde    is intended primarily for calendar-based uses rather than precise time
15512870Sphk    operations.
15612870Sphk  +/
15712870Sphkstruct DateTime
15811497Sphk{
159public:
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
3502private:
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 +/
3758struct Date
3759{
3760public:
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
8067private:
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
8085package:
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+/
8309struct TimeOfDay
8310{
8311public:
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
9475private:
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
9605package:
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  +/
9643bool valid(string units)(int value) @safe pure nothrow @nogc
9644if (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  +/
9677bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9678if (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  +/
9704void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9705if (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  +/
9766void enforceValid(string units)
9767                 (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9768if (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  +/
9798int 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  +/
9883int 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 +/
9965bool 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  +/
10027template 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
10039private:
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  +/
10110bool 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 +/
10145int 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 +/
10212template CmpTimeUnits(string lhs, string rhs)
10213if (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.
10227private 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
10257package:
10258
10259
10260/+
10261    Array of the short (three letter) names of each month.
10262  +/
10263immutable 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 +/
10283ubyte maxDay(int year, int month) @safe pure nothrow @nogc
10284in
10285{
10286    assert(valid!"months"(month));
10287}
10288do
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  +/
10373long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
10374if (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  +/
10402DayOfWeek 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
10459private:
10460
10461enum daysInYear     = 365;  // The number of days in a non-leap year.
10462enum daysInLeapYear = 366;  // The numbef or days in a leap year.
10463enum daysIn4Years   = daysInYear * 3 + daysInLeapYear;  // Number of days in 4 years.
10464enum daysIn100Years = daysIn4Years * 25 - 1;  // The number of days in 100 years.
10465enum 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  +/
10470immutable 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  +/
10475immutable 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  +/
10481string 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  +/
10515Month monthFromString(T)(T monthStr) @safe pure
10516if (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
10570version (StdUnittest)
10571{
10572private @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