1 // Written in the D programming language
2 
3 /++
4     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
5     Authors:   Jonathan M Davis
6     Source:    $(PHOBOSSRC std/datetime/_date.d)
7 +/
8 module std.datetime.date;
9 
10 import core.time;
11 import std.traits : isSomeString, Unqual;
12 import std.typecons : Flag;
13 
14 version (unittest) import std.exception : assertThrown;
15 
16 
17 @safe unittest
18 {
19     initializeTests();
20 }
21 
22 
23 /++
24     Exception type used by std.datetime. It's an alias to
25     $(REF TimeException,core,time). Either can be caught without concern about
26     which module it came from.
27   +/
28 alias DateTimeException = TimeException;
29 
30 
31 /++
32     Represents the 12 months of the Gregorian year (January is 1).
33   +/
34 enum Month : ubyte
35 {
36     jan = 1, ///
37     feb,     ///
38     mar,     ///
39     apr,     ///
40     may,     ///
41     jun,     ///
42     jul,     ///
43     aug,     ///
44     sep,     ///
45     oct,     ///
46     nov,     ///
47     dec      ///
48 }
49 
50 
51 /++
52     Represents the 7 days of the Gregorian week (Sunday is 0).
53   +/
54 enum DayOfWeek : ubyte
55 {
56     sun = 0, ///
57     mon,     ///
58     tue,     ///
59     wed,     ///
60     thu,     ///
61     fri,     ///
62     sat      ///
63 }
64 
65 
66 /++
67     In some date calculations, adding months or years can cause the date to fall
68     on a day of the month which is not valid (e.g. February 29th 2001 or
69     June 31st 2000). If overflow is allowed (as is the default), then the month
70     will be incremented accordingly (so, February 29th 2001 would become
71     March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
72     is not allowed, then the day will be adjusted to the last valid day in that
73     month (so, February 29th 2001 would become February 28th 2001 and
74     June 31st 2000 would become June 30th 2000).
75 
76     AllowDayOverflow only applies to calculations involving months or years.
77 
78     If set to $(D AllowDayOverflow.no), then day overflow is not allowed.
79 
80     Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is
81     allowed.
82   +/
83 alias AllowDayOverflow = Flag!"allowDayOverflow";
84 
85 
86 /++
87     Array of the strings representing time units, starting with the smallest
88     unit and going to the largest. It does not include $(D "nsecs").
89 
90    Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)),
91    $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"),
92    $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and
93    $(D "years")
94   +/
95 immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
96                                   "hours", "days", "weeks", "months", "years"];
97 
98 
99 /++
100    Combines the $(REF Date,std,datetime,date) and
101    $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
102    both the date and the time. It is optimized for calendar-based operations and
103    has no concept of time zone. For an object which is optimized for time
104    operations based on the system time, use $(REF SysTime,std,datetime,systime).
105    $(REF SysTime,std,datetime,systime) has a concept of time zone and has much
106    higher precision (hnsecs). $(D DateTime) is intended primarily for
107    calendar-based uses rather than precise time operations.
108   +/
109 struct DateTime
110 {
111 public:
112 
113     /++
114         Params:
115             date = The date portion of $(LREF DateTime).
116             tod  = The time portion of $(LREF DateTime).
117       +/
118     this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
119     {
120         _date = date;
121         _tod = tod;
122     }
123 
124     @safe unittest
125     {
126         {
127             auto dt = DateTime.init;
128             assert(dt._date == Date.init);
129             assert(dt._tod == TimeOfDay.init);
130         }
131 
132         {
133             auto dt = DateTime(Date(1999, 7 ,6));
134             assert(dt._date == Date(1999, 7, 6));
135             assert(dt._tod == TimeOfDay.init);
136         }
137 
138         {
139             auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33));
140             assert(dt._date == Date(1999, 7, 6));
141             assert(dt._tod == TimeOfDay(12, 30, 33));
142         }
143     }
144 
145 
146     /++
147         Params:
148             year   = The year portion of the date.
149             month  = The month portion of the date (January is 1).
150             day    = The day portion of the date.
151             hour   = The hour portion of the time;
152             minute = The minute portion of the time;
153             second = The second portion of the time;
154       +/
155     this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure
156     {
157         _date = Date(year, month, day);
158         _tod = TimeOfDay(hour, minute, second);
159     }
160 
161     @safe unittest
162     {
163         {
164             auto dt = DateTime(1999, 7 ,6);
165             assert(dt._date == Date(1999, 7, 6));
166             assert(dt._tod == TimeOfDay.init);
167         }
168 
169         {
170             auto dt = DateTime(1999, 7 ,6, 12, 30, 33);
171             assert(dt._date == Date(1999, 7, 6));
172             assert(dt._tod == TimeOfDay(12, 30, 33));
173         }
174     }
175 
176 
177     /++
178         Compares this $(LREF DateTime) with the given $(D DateTime.).
179 
180         Returns:
181             $(BOOKTABLE,
182             $(TR $(TD this < rhs) $(TD < 0))
183             $(TR $(TD this == rhs) $(TD 0))
184             $(TR $(TD this > rhs) $(TD > 0))
185             )
186      +/
opCmpDateTime187     int opCmp(in DateTime rhs) const @safe pure nothrow @nogc
188     {
189         immutable dateResult = _date.opCmp(rhs._date);
190 
191         if (dateResult != 0)
192             return dateResult;
193 
194         return _tod.opCmp(rhs._tod);
195     }
196 
197     @safe unittest
198     {
199         // Test A.D.
200         assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0);
201 
202         assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0);
203         assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0);
204         assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0);
205 
206         assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0);
207         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0);
208 
209         assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0);
210 
211         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
212         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
213         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
214         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
215         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0);
216         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
217 
218         assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
219         assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
220         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
221         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
222         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
223         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
224 
225 
226         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp(
227                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0);
228         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp(
229                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0);
230         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp(
231                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0);
232         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
233                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
234 
235         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp(
236                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0);
237         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
238                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
239 
240         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp(
241                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0);
242         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
243                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
244 
245         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
246                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
247         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
248                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
249         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
250                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
251         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
252                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
253         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
254                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0);
255         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
256                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
257 
258         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
259                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
260         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
261                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
262         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
263                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
264         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
265                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
266 
267         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
268                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
269         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
270                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
271 
272         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
273                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
274         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
275                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
276         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
277                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
278         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
279                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
280         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
281                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
282         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
283                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
284 
285         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
286                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
287         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
288                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
289         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
290                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
291         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
292                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
294                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
295         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
296                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
297 
298         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
299                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
300         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
301                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
302         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
303                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0);
304         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
305                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
306         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
307                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
308         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
309                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
310 
311         // Test B.C.
312         assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp(
313                    DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0);
314         assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
315                    DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0);
316         assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp(
317                    DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0);
318 
319         assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
320                    DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0);
321         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
322                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
323 
324         assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
325                    DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0);
326 
327         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
328                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
329         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
330                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
331         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
332                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
333         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
334                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
335         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
336                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
337         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
338                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
339 
340         assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
341                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
342         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
343                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
344         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
345                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
346         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
347                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
348         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
349                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
350         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
351                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
352 
353         // Test Both
354         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
355                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
356         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
357                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
358 
359         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
360                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
361         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
362                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0);
363 
364         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
365                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
366         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
367                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
368 
369         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
370                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
371         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
372                    DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0);
373 
374         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
375                    DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0);
376         assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp(
377                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
378 
379         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
380         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
381         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
382         assert(dt.opCmp(dt) == 0);
383         assert(dt.opCmp(cdt) == 0);
384         assert(dt.opCmp(idt) == 0);
385         assert(cdt.opCmp(dt) == 0);
386         assert(cdt.opCmp(cdt) == 0);
387         assert(cdt.opCmp(idt) == 0);
388         assert(idt.opCmp(dt) == 0);
389         assert(idt.opCmp(cdt) == 0);
390         assert(idt.opCmp(idt) == 0);
391     }
392 
393 
394     /++
395         The date portion of $(LREF DateTime).
396       +/
397     @property Date date() const @safe pure nothrow @nogc
398     {
399         return _date;
400     }
401 
402     @safe unittest
403     {
404         {
405             auto dt = DateTime.init;
406             assert(dt.date == Date.init);
407         }
408 
409         {
410             auto dt = DateTime(Date(1999, 7, 6));
411             assert(dt.date == Date(1999, 7, 6));
412         }
413 
414         const cdt = DateTime(1999, 7, 6);
415         immutable idt = DateTime(1999, 7, 6);
416         assert(cdt.date == Date(1999, 7, 6));
417         assert(idt.date == Date(1999, 7, 6));
418     }
419 
420 
421     /++
422         The date portion of $(LREF DateTime).
423 
424         Params:
425             date = The Date to set this $(LREF DateTime)'s date portion to.
426       +/
427     @property void date(in Date date) @safe pure nothrow @nogc
428     {
429         _date = date;
430     }
431 
432     @safe unittest
433     {
434         auto dt = DateTime.init;
435         dt.date = Date(1999, 7, 6);
436         assert(dt._date == Date(1999, 7, 6));
437         assert(dt._tod == TimeOfDay.init);
438 
439         const cdt = DateTime(1999, 7, 6);
440         immutable idt = DateTime(1999, 7, 6);
441         static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1)));
442         static assert(!__traits(compiles, idt.date = Date(2010, 1, 1)));
443     }
444 
445 
446     /++
447         The time portion of $(LREF DateTime).
448       +/
timeOfDayDateTime449     @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
450     {
451         return _tod;
452     }
453 
454     @safe unittest
455     {
456         {
457             auto dt = DateTime.init;
458             assert(dt.timeOfDay == TimeOfDay.init);
459         }
460 
461         {
462             auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33));
463             assert(dt.timeOfDay == TimeOfDay(12, 30, 33));
464         }
465 
466         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
467         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
468         assert(cdt.timeOfDay == TimeOfDay(12, 30, 33));
469         assert(idt.timeOfDay == TimeOfDay(12, 30, 33));
470     }
471 
472 
473     /++
474         The time portion of $(LREF DateTime).
475 
476         Params:
477             tod = The $(REF TimeOfDay,std,datetime,date) to set this
478                   $(LREF DateTime)'s time portion to.
479       +/
timeOfDayDateTime480     @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow @nogc
481     {
482         _tod = tod;
483     }
484 
485     @safe unittest
486     {
487         auto dt = DateTime.init;
488         dt.timeOfDay = TimeOfDay(12, 30, 33);
489         assert(dt._date == Date.init);
490         assert(dt._tod == TimeOfDay(12, 30, 33));
491 
492         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
493         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
494         static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33)));
495         static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33)));
496     }
497 
498 
499     /++
500         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
501         are B.C.
502      +/
503     @property short year() const @safe pure nothrow @nogc
504     {
505         return _date.year;
506     }
507 
508     @safe unittest
509     {
510         assert(Date.init.year == 1);
511         assert(Date(1999, 7, 6).year == 1999);
512         assert(Date(-1999, 7, 6).year == -1999);
513 
514         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
515         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
516         assert(idt.year == 1999);
517         assert(idt.year == 1999);
518     }
519 
520 
521     /++
522         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
523         are B.C.
524 
525         Params:
526             year = The year to set this $(LREF DateTime)'s year to.
527 
528         Throws:
529             $(REF DateTimeException,std,datetime,date) if the new year is not
530             a leap year and if the resulting date would be on February 29th.
531      +/
532     @property void year(int year) @safe pure
533     {
534         _date.year = year;
535     }
536 
537     ///
538     @safe unittest
539     {
540         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
541         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
542         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
543     }
544 
545     @safe unittest
546     {
547         static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__)
548         {
549             dt.year = year;
550             assert(dt == expected);
551         }
552 
553         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
554                1999,
555                DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)));
556         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
557                0,
558                DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)));
559         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
560                -1999,
561                DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)));
562 
563         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
564         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
565         static assert(!__traits(compiles, cdt.year = 7));
566         static assert(!__traits(compiles, idt.year = 7));
567     }
568 
569 
570     /++
571         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
572 
573         Throws:
574             $(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
575      +/
yearBCDateTime576     @property short yearBC() const @safe pure
577     {
578         return _date.yearBC;
579     }
580 
581     ///
582     @safe unittest
583     {
584         assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
585         assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
586         assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
587     }
588 
589     @safe unittest
590     {
591         assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
592 
593         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
594         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
595         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
596         dt.yearBC = 12;
597         assert(dt.yearBC == 12);
598         static assert(!__traits(compiles, cdt.yearBC = 12));
599         static assert(!__traits(compiles, idt.yearBC = 12));
600     }
601 
602 
603     /++
604         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
605 
606         Params:
607             year = The year B.C. to set this $(LREF DateTime)'s year to.
608 
609         Throws:
610             $(REF DateTimeException,std,datetime,date) if a non-positive value
611             is given.
612      +/
yearBCDateTime613     @property void yearBC(int year) @safe pure
614     {
615         _date.yearBC = year;
616     }
617 
618     ///
619     @safe unittest
620     {
621         auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
622         dt.yearBC = 1;
623         assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
624 
625         dt.yearBC = 10;
626         assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
627     }
628 
629     @safe unittest
630     {
631         assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1))));
632 
633         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
634         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
635         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
636         dt.yearBC = 12;
637         assert(dt.yearBC == 12);
638         static assert(!__traits(compiles, cdt.yearBC = 12));
639         static assert(!__traits(compiles, idt.yearBC = 12));
640     }
641 
642 
643     /++
644         Month of a Gregorian Year.
645      +/
646     @property Month month() const @safe pure nothrow @nogc
647     {
648         return _date.month;
649     }
650 
651     ///
652     @safe unittest
653     {
654         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
655         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
656         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
657     }
658 
659     @safe unittest
660     {
661         assert(DateTime.init.month == 1);
662         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
663         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
664 
665         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
666         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
667         assert(cdt.month == 7);
668         assert(idt.month == 7);
669     }
670 
671 
672     /++
673         Month of a Gregorian Year.
674 
675         Params:
676             month = The month to set this $(LREF DateTime)'s month to.
677 
678         Throws:
679             $(REF DateTimeException,std,datetime,date) if the given month is
680             not a valid month.
681      +/
682     @property void month(Month month) @safe pure
683     {
684         _date.month = month;
685     }
686 
687     @safe unittest
688     {
689         static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__)
690         {
691             dt.month = month;
692             assert(expected != DateTime.init);
693             assert(dt == expected);
694         }
695 
696         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0));
697         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13));
698 
699         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
700                cast(Month) 7,
701                DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)));
702         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)),
703                cast(Month) 7,
704                DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)));
705 
706         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
707         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
708         static assert(!__traits(compiles, cdt.month = 12));
709         static assert(!__traits(compiles, idt.month = 12));
710     }
711 
712 
713     /++
714         Day of a Gregorian Month.
715      +/
dayDateTime716     @property ubyte day() const @safe pure nothrow @nogc
717     {
718         return _date.day;
719     }
720 
721     ///
722     @safe unittest
723     {
724         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
725         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
726         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
727     }
728 
729     @safe unittest
730     {
731         import std.format : format;
732         import std.range : chain;
733 
testDateTime734         static void test(DateTime dateTime, int expected)
735         {
736             assert(dateTime.day == expected, format("Value given: %s", dateTime));
737         }
738 
foreachDateTime739         foreach (year; chain(testYearsBC, testYearsAD))
740         {
741             foreach (md; testMonthDays)
742             {
743                 foreach (tod; testTODs)
744                     test(DateTime(Date(year, md.month, md.day), tod), md.day);
745             }
746         }
747 
748         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
749         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
750         assert(cdt.day == 6);
751         assert(idt.day == 6);
752     }
753 
754 
755     /++
756         Day of a Gregorian Month.
757 
758         Params:
759             day = The day of the month to set this $(LREF DateTime)'s day to.
760 
761         Throws:
762             $(REF DateTimeException,std,datetime,date) if the given day is not
763             a valid day of the current month.
764      +/
dayDateTime765     @property void day(int day) @safe pure
766     {
767         _date.day = day;
768     }
769 
770     @safe unittest
771     {
772         import std.exception : assertNotThrown;
773 
774         static void testDT(DateTime dt, int day)
775         {
776             dt.day = day;
777         }
778 
779         // Test A.D.
780         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0));
781         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32));
782         assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29));
783         assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30));
784         assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32));
785         assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31));
786         assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32));
787         assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31));
788         assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32));
789         assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32));
790         assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31));
791         assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32));
792         assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31));
793         assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32));
794 
795         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31));
796         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28));
797         assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29));
798         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31));
799         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30));
800         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31));
801         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30));
802         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31));
803         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31));
804         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30));
805         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31));
806         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30));
807         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31));
808 
809         {
810             auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22));
811             dt.day = 6;
812             assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22)));
813         }
814 
815         // Test B.C.
816         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0));
817         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32));
818         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29));
819         assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30));
820         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32));
821         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31));
822         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32));
823         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31));
824         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32));
825         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32));
826         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31));
827         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32));
828         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31));
829         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32));
830 
831         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31));
832         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28));
833         assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29));
834         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31));
835         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30));
836         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31));
837         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30));
838         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31));
839         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31));
840         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30));
841         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31));
842         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30));
843         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31));
844 
845         auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22));
846         dt.day = 6;
847         assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22)));
848 
849         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
850         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
851         static assert(!__traits(compiles, cdt.day = 27));
852         static assert(!__traits(compiles, idt.day = 27));
853     }
854 
855 
856     /++
857         Hours past midnight.
858      +/
859     @property ubyte hour() const @safe pure nothrow @nogc
860     {
861         return _tod.hour;
862     }
863 
864     @safe unittest
865     {
866         assert(DateTime.init.hour == 0);
867         assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12);
868 
869         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
870         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
871         assert(cdt.hour == 12);
872         assert(idt.hour == 12);
873     }
874 
875 
876     /++
877         Hours past midnight.
878 
879         Params:
880             hour = The hour of the day to set this $(LREF DateTime)'s hour to.
881 
882         Throws:
883             $(REF DateTimeException,std,datetime,date) if the given hour would
884             result in an invalid $(LREF DateTime).
885      +/
886     @property void hour(int hour) @safe pure
887     {
888         _tod.hour = hour;
889     }
890 
891     @safe unittest
892     {
893         assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}());
894 
895         auto dt = DateTime.init;
896         dt.hour = 12;
897         assert(dt == DateTime(1, 1, 1, 12, 0, 0));
898 
899         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
900         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
901         static assert(!__traits(compiles, cdt.hour = 27));
902         static assert(!__traits(compiles, idt.hour = 27));
903     }
904 
905 
906     /++
907         Minutes past the hour.
908      +/
minuteDateTime909     @property ubyte minute() const @safe pure nothrow @nogc
910     {
911         return _tod.minute;
912     }
913 
914     @safe unittest
915     {
916         assert(DateTime.init.minute == 0);
917         assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30);
918 
919         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
920         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
921         assert(cdt.minute == 30);
922         assert(idt.minute == 30);
923     }
924 
925 
926     /++
927         Minutes past the hour.
928 
929         Params:
930             minute = The minute to set this $(LREF DateTime)'s minute to.
931 
932         Throws:
933             $(REF DateTimeException,std,datetime,date) if the given minute
934             would result in an invalid $(LREF DateTime).
935      +/
minuteDateTime936     @property void minute(int minute) @safe pure
937     {
938         _tod.minute = minute;
939     }
940 
941     @safe unittest
942     {
943         assertThrown!DateTimeException((){DateTime.init.minute = 60;}());
944 
945         auto dt = DateTime.init;
946         dt.minute = 30;
947         assert(dt == DateTime(1, 1, 1, 0, 30, 0));
948 
949         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
950         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
951         static assert(!__traits(compiles, cdt.minute = 27));
952         static assert(!__traits(compiles, idt.minute = 27));
953     }
954 
955 
956     /++
957         Seconds past the minute.
958      +/
959     @property ubyte second() const @safe pure nothrow @nogc
960     {
961         return _tod.second;
962     }
963 
964     @safe unittest
965     {
966         assert(DateTime.init.second == 0);
967         assert(DateTime(1, 1, 1, 0, 0, 33).second == 33);
968 
969         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
970         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
971         assert(cdt.second == 33);
972         assert(idt.second == 33);
973     }
974 
975 
976     /++
977         Seconds past the minute.
978 
979         Params:
980             second = The second to set this $(LREF DateTime)'s second to.
981 
982         Throws:
983             $(REF DateTimeException,std,datetime,date) if the given seconds
984             would result in an invalid $(LREF DateTime).
985      +/
986     @property void second(int second) @safe pure
987     {
988         _tod.second = second;
989     }
990 
991     @safe unittest
992     {
993         assertThrown!DateTimeException((){DateTime.init.second = 60;}());
994 
995         auto dt = DateTime.init;
996         dt.second = 33;
997         assert(dt == DateTime(1, 1, 1, 0, 0, 33));
998 
999         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1000         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1001         static assert(!__traits(compiles, cdt.second = 27));
1002         static assert(!__traits(compiles, idt.second = 27));
1003     }
1004 
1005 
1006     /++
1007         Adds the given number of years or months to this $(LREF DateTime). A
1008         negative number will subtract.
1009 
1010         Note that if day overflow is allowed, and the date with the adjusted
1011         year/month overflows the number of days in the new month, then the month
1012         will be incremented by one, and the day set to the number of days
1013         overflowed. (e.g. if the day were 31 and the new month were June, then
1014         the month would be incremented to July, and the new day would be 1). If
1015         day overflow is not allowed, then the day will be set to the last valid
1016         day in the month (e.g. June 31st would become June 30th).
1017 
1018         Params:
1019             units         = The type of units to add ("years" or "months").
1020             value         = The number of months or years to add to this
1021                             $(LREF DateTime).
1022             allowOverflow = Whether the days should be allowed to overflow,
1023                             causing the month to increment.
1024       +/
1025     ref DateTime add(string units)
1026                     (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1027         if (units == "years" || units == "months")
1028     {
1029         _date.add!units(value, allowOverflow);
1030         return this;
1031     }
1032 
1033     ///
1034     @safe unittest
1035     {
1036         auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
1037         dt1.add!"months"(11);
1038         assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
1039 
1040         auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
1041         dt2.add!"months"(-11);
1042         assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
1043 
1044         auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
1045         dt3.add!"years"(1);
1046         assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
1047 
1048         auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
1049         dt4.add!"years"(1, AllowDayOverflow.no);
1050         assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
1051     }
1052 
1053     @safe unittest
1054     {
1055         auto dt = DateTime(2000, 1, 31);
1056         dt.add!"years"(7).add!"months"(-4);
1057         assert(dt == DateTime(2006, 10, 1));
1058 
1059         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1060         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1061         static assert(!__traits(compiles, cdt.add!"years"(4)));
1062         static assert(!__traits(compiles, idt.add!"years"(4)));
1063         static assert(!__traits(compiles, cdt.add!"months"(4)));
1064         static assert(!__traits(compiles, idt.add!"months"(4)));
1065     }
1066 
1067 
1068     /++
1069         Adds the given number of years or months to this $(LREF DateTime). A
1070         negative number will subtract.
1071 
1072         The difference between rolling and adding is that rolling does not
1073         affect larger units. Rolling a $(LREF DateTime) 12 months
1074         gets the exact same $(LREF DateTime). However, the days can still be
1075         affected due to the differing number of days in each month.
1076 
1077         Because there are no units larger than years, there is no difference
1078         between adding and rolling years.
1079 
1080         Params:
1081             units         = The type of units to add ("years" or "months").
1082             value         = The number of months or years to add to this
1083                             $(LREF DateTime).
1084             allowOverflow = Whether the days should be allowed to overflow,
1085                             causing the month to increment.
1086       +/
1087     ref DateTime roll(string units)
1088                      (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1089         if (units == "years" || units == "months")
1090     {
1091         _date.roll!units(value, allowOverflow);
1092         return this;
1093     }
1094 
1095     ///
1096     @safe unittest
1097     {
1098         auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
1099         dt1.roll!"months"(1);
1100         assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
1101 
1102         auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
1103         dt2.roll!"months"(-1);
1104         assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
1105 
1106         auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
1107         dt3.roll!"months"(1);
1108         assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
1109 
1110         auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
1111         dt4.roll!"months"(1, AllowDayOverflow.no);
1112         assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
1113 
1114         auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
1115         dt5.roll!"years"(1);
1116         assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
1117 
1118         auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
1119         dt6.roll!"years"(1, AllowDayOverflow.no);
1120         assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
1121     }
1122 
1123     @safe unittest
1124     {
1125         auto dt = DateTime(2000, 1, 31);
1126         dt.roll!"years"(7).roll!"months"(-4);
1127         assert(dt == DateTime(2007, 10, 1));
1128 
1129         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1130         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1131         static assert(!__traits(compiles, cdt.roll!"years"(4)));
1132         static assert(!__traits(compiles, idt.roll!"years"(4)));
1133         static assert(!__traits(compiles, cdt.roll!"months"(4)));
1134         static assert(!__traits(compiles, idt.roll!"months"(4)));
1135     }
1136 
1137 
1138     /++
1139         Adds the given number of units to this $(LREF DateTime). A negative
1140         number will subtract.
1141 
1142         The difference between rolling and adding is that rolling does not
1143         affect larger units. For instance, rolling a $(LREF DateTime) one
1144         year's worth of days gets the exact same $(LREF DateTime).
1145 
1146         Accepted units are $(D "days"), $(D "minutes"), $(D "hours"),
1147         $(D "minutes"), and $(D "seconds").
1148 
1149         Params:
1150             units = The units to add.
1151             value = The number of $(D_PARAM units) to add to this
1152                     $(LREF DateTime).
1153       +/
1154     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1155         if (units == "days")
1156     {
1157         _date.roll!"days"(value);
1158         return this;
1159     }
1160 
1161     ///
1162     @safe unittest
1163     {
1164         auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
1165         dt1.roll!"days"(1);
1166         assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
1167         dt1.roll!"days"(365);
1168         assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
1169         dt1.roll!"days"(-32);
1170         assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
1171 
1172         auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
1173         dt2.roll!"hours"(1);
1174         assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
1175 
1176         auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
1177         dt3.roll!"seconds"(-1);
1178         assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
1179     }
1180 
1181     @safe unittest
1182     {
1183         auto dt = DateTime(2000, 1, 31);
1184         dt.roll!"days"(7).roll!"days"(-4);
1185         assert(dt == DateTime(2000, 1, 3));
1186 
1187         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1188         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1189         static assert(!__traits(compiles, cdt.roll!"days"(4)));
1190         static assert(!__traits(compiles, idt.roll!"days"(4)));
1191     }
1192 
1193 
1194     // Shares documentation with "days" version.
1195     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1196         if (units == "hours" ||
1197             units == "minutes" ||
1198             units == "seconds")
1199     {
1200         _tod.roll!units(value);
1201         return this;
1202     }
1203 
1204     // Test roll!"hours"().
1205     @safe unittest
1206     {
1207         static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__)
1208         {
1209             orig.roll!"hours"(hours);
1210             assert(orig == expected);
1211         }
1212 
1213         // Test A.D.
1214         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1215                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1216         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1217                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1218         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1219                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1220         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1221                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1222         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1223                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1224         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1225                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1226         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1227                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1228         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1229                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1230         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1231                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1232         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1233                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1234         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1235                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1236         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1237                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1238         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1239                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1240         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1241                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1242         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1243                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1244         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1245                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1246         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1247                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1248         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1249                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1250         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1251                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1252         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1253                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1254         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1255                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1256         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1257                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1258         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1259                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1260         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1261                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1262         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1263                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1264         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1265                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1266 
1267         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1268                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1269         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1270                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1271         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1272                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1273         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1274                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1275         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1276                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1277         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1278                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1279         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1280                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1281         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1282                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1283         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1284                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1285         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1286                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1287         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1288                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1289         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1290                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1291         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1292                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1293         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1294                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1295         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1296                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1297         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1298                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1299         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1300                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1301         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1302                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1303         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1304                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1305         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1306                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1307         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1308                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1309         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1310                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1311         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1312                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1313         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1314                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1315         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1316                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1317 
1318         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1319                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1320         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1321                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1322         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1323                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1324 
1325         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1326                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1327         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1328                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1329         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1330                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1331 
1332         testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1333                DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33)));
1334         testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1335                DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33)));
1336 
1337         testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1,
1338                DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33)));
1339         testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1340                DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33)));
1341 
1342         testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25,
1343                DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33)));
1344         testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25,
1345                DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33)));
1346 
1347         testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1348                DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33)));
1349         testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1350                DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33)));
1351 
1352         // Test B.C.
1353         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1354                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1355         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1356                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1357         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1358                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1359         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1360                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1361         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1362                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1363         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1364                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1365         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1366                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1367         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1368                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1369         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1370                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1371         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1372                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1373         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1374                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1375         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1376                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1377         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1378                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1379         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1380                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1381         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1382                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1383         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1384                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1385         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1386                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1387         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1388                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1389         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1390                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1391         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1392                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1393         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1394                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1395         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1396                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1397         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1398                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1399         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1400                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1401         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1402                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1403         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1404                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1405 
1406         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1407                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1408         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1409                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1410         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1411                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1412         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1413                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1414         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1415                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1416         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1417                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1418         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1419                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1420         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1421                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1422         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1423                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1424         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1425                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1426         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1427                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1428         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1429                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1430         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1431                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1432         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1433                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1434         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1435                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1436         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1437                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1438         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1439                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1440         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1441                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1442         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1443                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1444         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1445                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1446         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1447                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1448         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1449                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1450         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1451                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1452         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1453                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1454         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1455                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1456 
1457         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1458                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1459         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1460                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1461         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1462                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1463 
1464         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1465                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1466         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1467                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1468         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1469                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1470 
1471         testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1472                DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33)));
1473         testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1474                DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33)));
1475 
1476         testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1,
1477                DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33)));
1478         testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1479                DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33)));
1480 
1481         testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25,
1482                DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33)));
1483         testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25,
1484                DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33)));
1485 
1486         testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1487                DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33)));
1488         testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1489                DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33)));
1490 
1491         // Test Both
1492         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546,
1493                DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33)));
1494         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546,
1495                DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33)));
1496 
1497         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1498         dt.roll!"hours"(27).roll!"hours"(-9);
1499         assert(dt == DateTime(2000, 1, 31, 3, 7, 6));
1500 
1501         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1502         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1503         static assert(!__traits(compiles, cdt.roll!"hours"(4)));
1504         static assert(!__traits(compiles, idt.roll!"hours"(4)));
1505     }
1506 
1507     // Test roll!"minutes"().
1508     @safe unittest
1509     {
1510         static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__)
1511         {
1512             orig.roll!"minutes"(minutes);
1513             assert(orig == expected);
1514         }
1515 
1516         // Test A.D.
1517         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1518                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1519         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1520                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1521         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1522                DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33)));
1523         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1524                DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33)));
1525         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1526                DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33)));
1527         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1528                DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33)));
1529         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1530                DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33)));
1531         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1532                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1533         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1534                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1535         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1536                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1537         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1538                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1539         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1540                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1541         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1542                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1543         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1544                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1545         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1546                DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33)));
1547 
1548         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1549                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1550         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1551                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1552         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1553                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1554         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1555                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1556         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1557                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1558         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1559                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1560         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1561                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1562         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1563                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1564 
1565         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1566                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1567         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1568                DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33)));
1569         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1570                DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33)));
1571         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1572                DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33)));
1573         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1574                DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33)));
1575         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1576                DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33)));
1577         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1578                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1579         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1580                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1581         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1582                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1583         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1584                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1585         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1586                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1587         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1588                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1589         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1590                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1591         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1592                DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33)));
1593 
1594         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1595                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1596         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1597                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1598         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1599                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1600         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1601                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1602         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1603                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1604         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1605                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1606         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1607                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1608         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1609                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1610 
1611         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1612                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1613         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1614                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1615         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1616                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1617 
1618         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1619                DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33)));
1620         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1621                DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)));
1622         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1623                DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33)));
1624 
1625         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1626                DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33)));
1627         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1628                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)));
1629         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1630                DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33)));
1631 
1632         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1633                DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33)));
1634         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1635                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)));
1636         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1637                DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33)));
1638 
1639         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1,
1640                DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33)));
1641         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0,
1642                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)));
1643         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1,
1644                DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33)));
1645 
1646         // Test B.C.
1647         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1648                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1649         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1650                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1651         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1652                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33)));
1653         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1654                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33)));
1655         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1656                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33)));
1657         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1658                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33)));
1659         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1660                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33)));
1661         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1662                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1663         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1664                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1665         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1666                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1667         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1668                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1669         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1670                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1671         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1672                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1673         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1674                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1675         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1676                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33)));
1677 
1678         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1679                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1680         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1681                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1682         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1683                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1684         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1685                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1686         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1687                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1688         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1689                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1690         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1691                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1692         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1693                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1694 
1695         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1696                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1697         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1698                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33)));
1699         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1700                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33)));
1701         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1702                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33)));
1703         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1704                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33)));
1705         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1706                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33)));
1707         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1708                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1709         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1710                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1711         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1712                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1713         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1714                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1715         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1716                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1717         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1718                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1719         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1720                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1721         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1722                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33)));
1723 
1724         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1725                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1726         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1727                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1728         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1729                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1730         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1731                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1732         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1733                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1734         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1735                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1736         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1737                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1738         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1739                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1740 
1741         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1742                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1743         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1744                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1745         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1746                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1747 
1748         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1749                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33)));
1750         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1751                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)));
1752         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1753                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33)));
1754 
1755         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1756                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33)));
1757         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1758                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)));
1759         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1760                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33)));
1761 
1762         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1763                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33)));
1764         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1765                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)));
1766         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1767                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33)));
1768 
1769         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1,
1770                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33)));
1771         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0,
1772                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)));
1773         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1,
1774                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33)));
1775 
1776         // Test Both
1777         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
1778                DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0)));
1779         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1,
1780                DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0)));
1781 
1782         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
1783                DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0)));
1784         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1,
1785                DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0)));
1786 
1787         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760,
1788                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
1789         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760,
1790                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1791 
1792         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782,
1793                DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33)));
1794         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782,
1795                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1796 
1797         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1798         dt.roll!"minutes"(92).roll!"minutes"(-292);
1799         assert(dt == DateTime(2000, 1, 31, 9, 47, 6));
1800 
1801         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1802         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1803         static assert(!__traits(compiles, cdt.roll!"minutes"(4)));
1804         static assert(!__traits(compiles, idt.roll!"minutes"(4)));
1805     }
1806 
1807     // Test roll!"seconds"().
1808     @safe unittest
1809     {
1810         static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__)
1811         {
1812             orig.roll!"seconds"(seconds);
1813             assert(orig == expected);
1814         }
1815 
1816         // Test A.D.
1817         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1818                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1819         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1820                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1821         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1822                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35)));
1823         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1824                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36)));
1825         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1826                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37)));
1827         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1828                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38)));
1829         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1830                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43)));
1831         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1832                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48)));
1833         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
1834                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1835         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
1836                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1837         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1838                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3)));
1839         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
1840                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1841         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1842                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1843         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
1844                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1845 
1846         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
1847                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1848         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
1849                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1850         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
1851                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1852         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
1853                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1854         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
1855                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1856         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
1857                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1858         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
1859                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1860         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
1861                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1862 
1863         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1864                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1865         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1866                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31)));
1867         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1868                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30)));
1869         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1870                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29)));
1871         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1872                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28)));
1873         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1874                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23)));
1875         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1876                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18)));
1877         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
1878                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1879         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
1880                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1881         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
1882                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58)));
1883         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
1884                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1885         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1886                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1887         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
1888                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1889 
1890         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
1891                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1892         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
1893                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1894         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
1895                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1896 
1897         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
1898                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1)));
1899         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
1900                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)));
1901         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
1902                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59)));
1903 
1904         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
1905                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1)));
1906         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
1907                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)));
1908         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
1909                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59)));
1910 
1911         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
1912                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0)));
1913         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
1914                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)));
1915         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
1916                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58)));
1917 
1918         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1,
1919                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0)));
1920         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0,
1921                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)));
1922         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1,
1923                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58)));
1924 
1925         // Test B.C.
1926         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1927                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1928         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1929                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1930         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1931                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35)));
1932         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1933                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36)));
1934         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1935                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37)));
1936         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1937                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38)));
1938         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1939                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43)));
1940         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1941                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48)));
1942         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
1943                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
1944         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
1945                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
1946         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1947                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3)));
1948         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
1949                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
1950         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1951                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1952         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
1953                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1954 
1955         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
1956                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
1957         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
1958                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
1959         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
1960                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
1961         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
1962                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
1963         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
1964                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
1965         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
1966                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1967         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
1968                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1969         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
1970                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1971 
1972         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1973                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
1974         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1975                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31)));
1976         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1977                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30)));
1978         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1979                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29)));
1980         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1981                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28)));
1982         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1983                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23)));
1984         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1985                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18)));
1986         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
1987                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
1988         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
1989                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
1990         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
1991                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58)));
1992         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
1993                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1994         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1995                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1996         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
1997                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
1998 
1999         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
2000                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2001         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
2002                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2003         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
2004                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2005 
2006         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
2007                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1)));
2008         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
2009                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)));
2010         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
2011                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59)));
2012 
2013         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
2014                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1)));
2015         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
2016                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)));
2017         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
2018                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59)));
2019 
2020         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
2021                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0)));
2022         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
2023                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)));
2024         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
2025                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58)));
2026 
2027         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1,
2028                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0)));
2029         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0,
2030                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)));
2031         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1,
2032                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58)));
2033 
2034         // Test Both
2035         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
2036                DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59)));
2037         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1,
2038                DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)));
2039 
2040         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
2041                DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59)));
2042         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1,
2043                DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)));
2044 
2045         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L,
2046                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
2047         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L,
2048                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2049 
2050         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L,
2051                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50)));
2052         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L,
2053                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2054 
2055         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2056         dt.roll!"seconds"(92).roll!"seconds"(-292);
2057         assert(dt == DateTime(2000, 1, 31, 9, 7, 46));
2058 
2059         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2060         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2061         static assert(!__traits(compiles, cdt.roll!"seconds"(4)));
2062         static assert(!__traits(compiles, idt.roll!"seconds"(4)));
2063     }
2064 
2065 
2066     /++
2067         Gives the result of adding or subtracting a $(REF Duration, core,time)
2068         from this $(LREF DateTime).
2069 
2070         The legal types of arithmetic for $(LREF DateTime) using this operator
2071         are
2072 
2073         $(BOOKTABLE,
2074         $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
2075         $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
2076         )
2077 
2078         Params:
2079             duration = The $(REF Duration, core,time) to add to or subtract from
2080                        this $(LREF DateTime).
2081       +/
2082     DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
2083         if (op == "+" || op == "-")
2084     {
2085         DateTime retval = this;
2086         immutable seconds = duration.total!"seconds";
2087         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
2088     }
2089 
2090     ///
2091     @safe unittest
2092     {
2093         import core.time : hours, seconds;
2094 
2095         assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
2096                DateTime(2016, 1, 1, 0, 0, 0));
2097 
2098         assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
2099                DateTime(2016, 1, 1, 0, 59, 59));
2100 
2101         assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
2102                DateTime(2015, 12, 31, 23, 59, 59));
2103 
2104         assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
2105                DateTime(2015, 12, 31, 23, 59, 59));
2106     }
2107 
2108     @safe unittest
2109     {
2110         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2111 
2112         assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2113         assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2114         assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2115         assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2116 
2117         assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2118         assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2119         assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2120         assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2121         assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2122         assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2123         assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2124         assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2125         assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2126         assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2127         assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2128         assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2129 
2130         assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2131         assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2132         assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2133         assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2134 
2135         assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2136         assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2137         assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2138         assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2139         assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2140         assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2141         assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2142         assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2143         assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2144         assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2145         assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2146         assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2147 
2148         auto duration = dur!"seconds"(12);
2149         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2150         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2151         assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2152         assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2153         assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2154         assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2155     }
2156 
2157 
2158     /++
2159         Gives the result of adding or subtracting a duration from this
2160         $(LREF DateTime), as well as assigning the result to this
2161         $(LREF DateTime).
2162 
2163         The legal types of arithmetic for $(LREF DateTime) using this operator
2164         are
2165 
2166         $(BOOKTABLE,
2167         $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime))
2168         $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime))
2169         )
2170 
2171         Params:
2172             duration = The duration to add to or subtract from this
2173                        $(LREF DateTime).
2174       +/
2175     ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
2176         if (op == "+" || op == "-")
2177     {
2178         import core.time : convert;
2179         import std.format : format;
2180 
2181         DateTime retval = this;
2182         immutable hnsecs = duration.total!"hnsecs";
2183 
2184         mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
2185     }
2186 
2187     @safe unittest
2188     {
2189         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
2190                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2191         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
2192                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2193         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) ==
2194                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2195         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) ==
2196                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2197 
2198         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) ==
2199                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2200         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) ==
2201                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2202         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) ==
2203                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2204         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) ==
2205                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2206         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) ==
2207                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2208         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) ==
2209                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2210         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) ==
2211                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2212         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) ==
2213                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2214         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) ==
2215                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2216         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) ==
2217                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2218         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) ==
2219                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2220         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) ==
2221                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2222 
2223         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) ==
2224                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2225         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) ==
2226                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2227         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) ==
2228                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2229         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) ==
2230                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2231 
2232         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) ==
2233                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2234         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) ==
2235                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2236         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) ==
2237                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2238         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) ==
2239                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2240         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) ==
2241                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2242         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) ==
2243                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2244         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) ==
2245                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2246         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) ==
2247                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2248         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) ==
2249                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2250         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) ==
2251                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2252         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) ==
2253                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2254         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) ==
2255                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2256 
2257         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2258         (dt += dur!"seconds"(92)) -= dur!"days"(-500);
2259         assert(dt == DateTime(2001, 6, 14, 9, 8, 38));
2260 
2261         auto duration = dur!"seconds"(12);
2262         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2263         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2264         static assert(!__traits(compiles, cdt += duration));
2265         static assert(!__traits(compiles, idt += duration));
2266         static assert(!__traits(compiles, cdt -= duration));
2267         static assert(!__traits(compiles, idt -= duration));
2268     }
2269 
2270 
2271     /++
2272         Gives the difference between two $(LREF DateTime)s.
2273 
2274         The legal types of arithmetic for $(LREF DateTime) using this operator are
2275 
2276         $(BOOKTABLE,
2277         $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
2278         )
2279       +/
2280     Duration opBinary(string op)(in DateTime rhs) const @safe pure nothrow @nogc
2281         if (op == "-")
2282     {
2283         immutable dateResult = _date - rhs.date;
2284         immutable todResult = _tod - rhs._tod;
2285 
2286         return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
2287     }
2288 
2289     @safe unittest
2290     {
2291         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
2292 
2293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
2294                dur!"seconds"(31_536_000));
2295         assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2296                dur!"seconds"(-31_536_000));
2297 
2298         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2299                dur!"seconds"(26_78_400));
2300         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) ==
2301                dur!"seconds"(-26_78_400));
2302 
2303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) ==
2304                dur!"seconds"(86_400));
2305         assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2306                dur!"seconds"(-86_400));
2307 
2308         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) ==
2309                dur!"seconds"(3600));
2310         assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2311                dur!"seconds"(-3600));
2312 
2313         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2314                dur!"seconds"(60));
2315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) ==
2316                dur!"seconds"(-60));
2317 
2318         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2319                dur!"seconds"(1));
2320         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) ==
2321                dur!"seconds"(-1));
2322 
2323         assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033));
2324         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033));
2325         assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367));
2326         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367));
2327 
2328         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2329         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2330         assert(dt - dt == Duration.zero);
2331         assert(cdt - dt == Duration.zero);
2332         assert(idt - dt == Duration.zero);
2333 
2334         assert(dt - cdt == Duration.zero);
2335         assert(cdt - cdt == Duration.zero);
2336         assert(idt - cdt == Duration.zero);
2337 
2338         assert(dt - idt == Duration.zero);
2339         assert(cdt - idt == Duration.zero);
2340         assert(idt - idt == Duration.zero);
2341     }
2342 
2343 
2344     /++
2345         Returns the difference between the two $(LREF DateTime)s in months.
2346 
2347         To get the difference in years, subtract the year property
2348         of two $(LREF DateTime)s. To get the difference in days or weeks,
2349         subtract the $(LREF DateTime)s themselves and use the
2350         $(REF Duration, core,time) that results. Because converting between
2351         months and smaller units requires a specific date (which
2352         $(REF Duration, core,time)s don't have), getting the difference in
2353         months requires some math using both the year and month properties, so
2354         this is a convenience function for getting the difference in months.
2355 
2356         Note that the number of days in the months or how far into the month
2357         either date is is irrelevant. It is the difference in the month property
2358         combined with the difference in years * 12. So, for instance,
2359         December 31st and January 1st are one month apart just as December 1st
2360         and January 31st are one month apart.
2361 
2362         Params:
2363             rhs = The $(LREF DateTime) to subtract from this one.
2364       +/
diffMonthsDateTime2365     int diffMonths(in DateTime rhs) const @safe pure nothrow @nogc
2366     {
2367         return _date.diffMonths(rhs._date);
2368     }
2369 
2370     ///
2371     @safe unittest
2372     {
2373         assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
2374                    DateTime(1999, 1, 31, 23, 59, 59)) == 1);
2375 
2376         assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
2377                    DateTime(1999, 2, 1, 12, 3, 42)) == -1);
2378 
2379         assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
2380                    DateTime(1999, 1, 1, 2, 4, 7)) == 2);
2381 
2382         assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
2383                    DateTime(1999, 3, 31, 0, 30, 58)) == -2);
2384     }
2385 
2386     @safe unittest
2387     {
2388         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2389         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2390         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2391         assert(dt.diffMonths(dt) == 0);
2392         assert(cdt.diffMonths(dt) == 0);
2393         assert(idt.diffMonths(dt) == 0);
2394 
2395         assert(dt.diffMonths(cdt) == 0);
2396         assert(cdt.diffMonths(cdt) == 0);
2397         assert(idt.diffMonths(cdt) == 0);
2398 
2399         assert(dt.diffMonths(idt) == 0);
2400         assert(cdt.diffMonths(idt) == 0);
2401         assert(idt.diffMonths(idt) == 0);
2402     }
2403 
2404 
2405     /++
2406         Whether this $(LREF DateTime) is in a leap year.
2407      +/
isLeapYearDateTime2408     @property bool isLeapYear() const @safe pure nothrow @nogc
2409     {
2410         return _date.isLeapYear;
2411     }
2412 
2413     @safe unittest
2414     {
2415         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2416         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2417         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2418         assert(!dt.isLeapYear);
2419         assert(!cdt.isLeapYear);
2420         assert(!idt.isLeapYear);
2421     }
2422 
2423 
2424     /++
2425         Day of the week this $(LREF DateTime) is on.
2426       +/
dayOfWeekDateTime2427     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
2428     {
2429         return _date.dayOfWeek;
2430     }
2431 
2432     @safe unittest
2433     {
2434         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2435         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2436         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2437         assert(dt.dayOfWeek == DayOfWeek.tue);
2438         assert(cdt.dayOfWeek == DayOfWeek.tue);
2439         assert(idt.dayOfWeek == DayOfWeek.tue);
2440     }
2441 
2442 
2443     /++
2444         Day of the year this $(LREF DateTime) is on.
2445       +/
dayOfYearDateTime2446     @property ushort dayOfYear() const @safe pure nothrow @nogc
2447     {
2448         return _date.dayOfYear;
2449     }
2450 
2451     ///
2452     @safe unittest
2453     {
2454         assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
2455         assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
2456         assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
2457     }
2458 
2459     @safe unittest
2460     {
2461         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2462         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2463         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2464         assert(dt.dayOfYear == 187);
2465         assert(cdt.dayOfYear == 187);
2466         assert(idt.dayOfYear == 187);
2467     }
2468 
2469 
2470     /++
2471         Day of the year.
2472 
2473         Params:
2474             day = The day of the year to set which day of the year this
2475                   $(LREF DateTime) is on.
2476       +/
dayOfYearDateTime2477     @property void dayOfYear(int day) @safe pure
2478     {
2479         _date.dayOfYear = day;
2480     }
2481 
2482     @safe unittest
2483     {
2484         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2485         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2486         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2487         dt.dayOfYear = 12;
2488         assert(dt.dayOfYear == 12);
2489         static assert(!__traits(compiles, cdt.dayOfYear = 12));
2490         static assert(!__traits(compiles, idt.dayOfYear = 12));
2491     }
2492 
2493 
2494     /++
2495         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2496      +/
dayOfGregorianCalDateTime2497     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
2498     {
2499         return _date.dayOfGregorianCal;
2500     }
2501 
2502     ///
2503     @safe unittest
2504     {
2505         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
2506         assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
2507         assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
2508 
2509         assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
2510         assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
2511         assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
2512 
2513         assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
2514         assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
2515     }
2516 
2517     @safe unittest
2518     {
2519         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2520         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2521         assert(cdt.dayOfGregorianCal == 729_941);
2522         assert(idt.dayOfGregorianCal == 729_941);
2523     }
2524 
2525 
2526     /++
2527         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2528         Setting this property does not affect the time portion of
2529         $(LREF DateTime).
2530 
2531         Params:
2532             days = The day of the Gregorian Calendar to set this $(LREF DateTime)
2533                    to.
2534      +/
dayOfGregorianCalDateTime2535     @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
2536     {
2537         _date.dayOfGregorianCal = days;
2538     }
2539 
2540     ///
2541     @safe unittest
2542     {
2543         auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
2544         dt.dayOfGregorianCal = 1;
2545         assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
2546 
2547         dt.dayOfGregorianCal = 365;
2548         assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
2549 
2550         dt.dayOfGregorianCal = 366;
2551         assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
2552 
2553         dt.dayOfGregorianCal = 0;
2554         assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
2555 
2556         dt.dayOfGregorianCal = -365;
2557         assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
2558 
2559         dt.dayOfGregorianCal = -366;
2560         assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
2561 
2562         dt.dayOfGregorianCal = 730_120;
2563         assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
2564 
2565         dt.dayOfGregorianCal = 734_137;
2566         assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
2567     }
2568 
2569     @safe unittest
2570     {
2571         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2572         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2573         static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7));
2574         static assert(!__traits(compiles, idt.dayOfGregorianCal = 7));
2575     }
2576 
2577 
2578     /++
2579         The ISO 8601 week of the year that this $(LREF DateTime) is in.
2580 
2581         See_Also:
2582             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2583       +/
isoWeekDateTime2584     @property ubyte isoWeek() const @safe pure nothrow
2585     {
2586         return _date.isoWeek;
2587     }
2588 
2589     @safe unittest
2590     {
2591         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2592         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2593         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2594         assert(dt.isoWeek == 27);
2595         assert(cdt.isoWeek == 27);
2596         assert(idt.isoWeek == 27);
2597     }
2598 
2599 
2600     /++
2601         $(LREF DateTime) for the last day in the month that this
2602         $(LREF DateTime) is in. The time portion of endOfMonth is always
2603         23:59:59.
2604       +/
endOfMonthDateTime2605     @property DateTime endOfMonth() const @safe pure nothrow
2606     {
2607         try
2608             return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
2609         catch (Exception e)
2610             assert(0, "DateTime constructor threw.");
2611     }
2612 
2613     ///
2614     @safe unittest
2615     {
2616         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
2617                DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
2618 
2619         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
2620                DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
2621 
2622         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
2623                DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
2624 
2625         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
2626                DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
2627     }
2628 
2629     @safe unittest
2630     {
2631         // Test A.D.
2632         assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59));
2633         assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59));
2634         assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59));
2635         assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59));
2636         assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59));
2637         assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59));
2638         assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59));
2639         assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2640         assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59));
2641         assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59));
2642         assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59));
2643         assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59));
2644         assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59));
2645 
2646         // Test B.C.
2647         assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59));
2648         assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59));
2649         assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59));
2650         assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59));
2651         assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59));
2652         assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59));
2653         assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59));
2654         assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59));
2655         assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59));
2656         assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59));
2657         assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59));
2658         assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59));
2659         assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59));
2660 
2661         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2662         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2663         assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2664         assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2665     }
2666 
2667 
2668     /++
2669         The last day in the month that this $(LREF DateTime) is in.
2670       +/
daysInMonthDateTime2671     @property ubyte daysInMonth() const @safe pure nothrow @nogc
2672     {
2673         return _date.daysInMonth;
2674     }
2675 
2676     ///
2677     @safe unittest
2678     {
2679         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
2680         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
2681         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
2682         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
2683     }
2684 
2685     @safe unittest
2686     {
2687         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2688         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2689         assert(cdt.daysInMonth == 31);
2690         assert(idt.daysInMonth == 31);
2691     }
2692 
2693 
2694     /++
2695         Whether the current year is a date in A.D.
2696       +/
isADDateTime2697     @property bool isAD() const @safe pure nothrow @nogc
2698     {
2699         return _date.isAD;
2700     }
2701 
2702     ///
2703     @safe unittest
2704     {
2705         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
2706         assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
2707         assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
2708         assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
2709     }
2710 
2711     @safe unittest
2712     {
2713         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2714         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2715         assert(cdt.isAD);
2716         assert(idt.isAD);
2717     }
2718 
2719 
2720     /++
2721         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
2722         $(LREF DateTime) at the given time. For example, prior to noon,
2723         1996-03-31 would be the Julian day number 2_450_173, so this function
2724         returns 2_450_173, while from noon onward, the julian day number would
2725         be 2_450_174, so this function returns 2_450_174.
2726       +/
julianDayDateTime2727     @property long julianDay() const @safe pure nothrow @nogc
2728     {
2729         if (_tod._hour < 12)
2730             return _date.julianDay - 1;
2731         else
2732             return _date.julianDay;
2733     }
2734 
2735     @safe unittest
2736     {
2737         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1);
2738         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0);
2739 
2740         assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424);
2741         assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425);
2742 
2743         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425);
2744         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426);
2745 
2746         assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160);
2747         assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161);
2748 
2749         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000);
2750         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001);
2751 
2752         assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973);
2753         assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974);
2754 
2755         assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173);
2756         assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174);
2757 
2758         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432);
2759         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433);
2760 
2761         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2762         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2763         assert(cdt.julianDay == 2_451_366);
2764         assert(idt.julianDay == 2_451_366);
2765     }
2766 
2767 
2768     /++
2769         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
2770         time on this date (since, the modified Julian day changes at midnight).
2771       +/
modJulianDayDateTime2772     @property long modJulianDay() const @safe pure nothrow @nogc
2773     {
2774         return _date.modJulianDay;
2775     }
2776 
2777     @safe unittest
2778     {
2779         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0);
2780         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0);
2781 
2782         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432);
2783         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432);
2784 
2785         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2786         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2787         assert(cdt.modJulianDay == 51_365);
2788         assert(idt.modJulianDay == 51_365);
2789     }
2790 
2791 
2792     /++
2793         Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS.
2794       +/
toISOStringDateTime2795     string toISOString() const @safe pure nothrow
2796     {
2797         import std.format : format;
2798         try
2799         {
2800             return format!("%sT%02d%02d%02d")(
2801                 _date.toISOString(),
2802                 _tod._hour,
2803                 _tod._minute,
2804                 _tod._second
2805             );
2806         }
2807         catch (Exception e)
2808         {
2809             assert(0, "format() threw.");
2810         }
2811     }
2812 
2813     ///
2814     @safe unittest
2815     {
2816         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
2817                "20100704T070612");
2818 
2819         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
2820                "19981225T021500");
2821 
2822         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
2823                "00000105T230959");
2824 
2825         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
2826                "-00040105T000002");
2827     }
2828 
2829     @safe unittest
2830     {
2831         // Test A.D.
2832         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000");
2833         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612");
2834         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459");
2835         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959");
2836         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101");
2837 
2838         // Test B.C.
2839         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204");
2840         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000");
2841         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612");
2842         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459");
2843         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959");
2844         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101");
2845 
2846         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2847         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2848         assert(cdt.toISOString() == "19990706T123033");
2849         assert(idt.toISOString() == "19990706T123033");
2850     }
2851 
2852 
2853     /++
2854         Converts this $(LREF DateTime) to a string with the format
2855         YYYY-MM-DDTHH:MM:SS.
2856       +/
toISOExtStringDateTime2857     string toISOExtString() const @safe pure nothrow
2858     {
2859         import std.format : format;
2860         try
2861         {
2862             return format!("%sT%02d:%02d:%02d")(
2863                 _date.toISOExtString(),
2864                 _tod._hour,
2865                 _tod._minute,
2866                 _tod._second
2867             );
2868         }
2869         catch (Exception e)
2870         {
2871             assert(0, "format() threw.");
2872         }
2873     }
2874 
2875     ///
2876     @safe unittest
2877     {
2878         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
2879                "2010-07-04T07:06:12");
2880 
2881         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
2882                "1998-12-25T02:15:00");
2883 
2884         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
2885                "0000-01-05T23:09:59");
2886 
2887         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
2888                "-0004-01-05T00:00:02");
2889     }
2890 
2891     @safe unittest
2892     {
2893         // Test A.D.
2894         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00");
2895         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12");
2896         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59");
2897         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59");
2898         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01");
2899 
2900         // Test B.C.
2901         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04");
2902         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00");
2903         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12");
2904         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59");
2905         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59");
2906         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01");
2907 
2908         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2909         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2910         assert(cdt.toISOExtString() == "1999-07-06T12:30:33");
2911         assert(idt.toISOExtString() == "1999-07-06T12:30:33");
2912     }
2913 
2914     /++
2915         Converts this $(LREF DateTime) to a string with the format
2916         YYYY-Mon-DD HH:MM:SS.
2917       +/
toSimpleStringDateTime2918     string toSimpleString() const @safe pure nothrow
2919     {
2920         import std.format : format;
2921         try
2922         {
2923             return format!("%s %02d:%02d:%02d")(
2924                 _date.toSimpleString(),
2925                 _tod._hour,
2926                 _tod._minute,
2927                 _tod._second
2928             );
2929         }
2930         catch (Exception e)
2931         {
2932             assert(0, "format() threw.");
2933         }
2934     }
2935 
2936     ///
2937     @safe unittest
2938     {
2939         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
2940                "2010-Jul-04 07:06:12");
2941 
2942         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
2943                "1998-Dec-25 02:15:00");
2944 
2945         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
2946                "0000-Jan-05 23:09:59");
2947 
2948         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
2949                "-0004-Jan-05 00:00:02");
2950     }
2951 
2952     @safe unittest
2953     {
2954         // Test A.D.
2955         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00");
2956         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12");
2957         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59");
2958         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59");
2959         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01");
2960 
2961         // Test B.C.
2962         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04");
2963         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00");
2964         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12");
2965         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59");
2966         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59");
2967         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01");
2968 
2969         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2970         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2971         assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33");
2972         assert(idt.toSimpleString() == "1999-Jul-06 12:30:33");
2973     }
2974 
2975 
2976     /++
2977         Converts this $(LREF DateTime) to a string.
2978 
2979         This function exists to make it easy to convert a $(LREF DateTime) to a
2980         string for code that does not care what the exact format is - just that
2981         it presents the information in a clear manner. It also makes it easy to
2982         simply convert a $(LREF DateTime) to a string when using functions such
2983         as `to!string`, `format`, or `writeln` which use toString to convert
2984         user-defined types. So, it is unlikely that much code will call
2985         toString directly.
2986 
2987         The format of the string is purposefully unspecified, and code that
2988         cares about the format of the string should use `toISOString`,
2989         `toISOExtString`, `toSimpleString`, or some other custom formatting
2990         function that explicitly generates the format that the code needs. The
2991         reason is that the code is then clear about what format it's using,
2992         making it less error-prone to maintain the code and interact with other
2993         software that consumes the generated strings. It's for this same reason
2994         that $(LREF DateTime) has no `fromString` function, whereas it does have
2995         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
2996 
2997         The format returned by toString may or may not change in the future.
2998       +/
toStringDateTime2999     string toString() const @safe pure nothrow
3000     {
3001         return toSimpleString();
3002     }
3003 
3004     @safe unittest
3005     {
3006         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3007         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3008         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3009         assert(dt.toString());
3010         assert(cdt.toString());
3011         assert(idt.toString());
3012     }
3013 
3014 
3015 
3016     /++
3017         Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
3018         Whitespace is stripped from the given string.
3019 
3020         Params:
3021             isoString = A string formatted in the ISO format for dates and times.
3022 
3023         Throws:
3024             $(REF DateTimeException,std,datetime,date) if the given string is
3025             not in the ISO format or if the resulting $(LREF DateTime) would not
3026             be valid.
3027       +/
3028     static DateTime fromISOString(S)(in S isoString) @safe pure
3029         if (isSomeString!S)
3030     {
3031         import std.algorithm.searching : countUntil;
3032         import std.exception : enforce;
3033         import std.format : format;
3034         import std.string : strip;
3035 
3036         auto str = strip(isoString);
3037 
3038         enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
3039         auto t = str.countUntil('T');
3040 
3041         enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
3042 
3043         immutable date = Date.fromISOString(str[0 .. t]);
3044         immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
3045 
3046         return DateTime(date, tod);
3047     }
3048 
3049     ///
3050     @safe unittest
3051     {
3052         assert(DateTime.fromISOString("20100704T070612") ==
3053                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3054 
3055         assert(DateTime.fromISOString("19981225T021500") ==
3056                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3057 
3058         assert(DateTime.fromISOString("00000105T230959") ==
3059                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3060 
3061         assert(DateTime.fromISOString("-00040105T000002") ==
3062                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3063 
3064         assert(DateTime.fromISOString(" 20100704T070612 ") ==
3065                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3066     }
3067 
3068     @safe unittest
3069     {
3070         assertThrown!DateTimeException(DateTime.fromISOString(""));
3071         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3072         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3073         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3074         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3075         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3076 
3077         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3078         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3079         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3080         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3081         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3082 
3083         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3084         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3085         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3086         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3087         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3088         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3089 
3090         assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
3091         assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
3092 
3093         assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01)));
3094         assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3095         assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3096         assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3097         assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3098         assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3099         assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3100     }
3101 
3102     // bug# 17801
3103     @safe unittest
3104     {
3105         import std.conv : to;
3106         import std.meta : AliasSeq;
3107         foreach (C; AliasSeq!(char, wchar, dchar))
3108         {
3109             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3110                 assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
3111         }
3112     }
3113 
3114 
3115     /++
3116         Creates a $(LREF DateTime) from a string with the format
3117         YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string.
3118 
3119         Params:
3120             isoExtString = A string formatted in the ISO Extended format for dates
3121                            and times.
3122 
3123         Throws:
3124             $(REF DateTimeException,std,datetime,date) if the given string is
3125             not in the ISO Extended format or if the resulting $(LREF DateTime)
3126             would not be valid.
3127       +/
3128     static DateTime fromISOExtString(S)(in S isoExtString) @safe pure
3129         if (isSomeString!(S))
3130     {
3131         import std.algorithm.searching : countUntil;
3132         import std.exception : enforce;
3133         import std.format : format;
3134         import std.string : strip;
3135 
3136         auto str = strip(isoExtString);
3137 
3138         enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
3139         auto t = str.countUntil('T');
3140 
3141         enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
3142 
3143         immutable date = Date.fromISOExtString(str[0 .. t]);
3144         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3145 
3146         return DateTime(date, tod);
3147     }
3148 
3149     ///
3150     @safe unittest
3151     {
3152         assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
3153                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3154 
3155         assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
3156                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3157 
3158         assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
3159                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3160 
3161         assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
3162                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3163 
3164         assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
3165                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3166     }
3167 
3168     @safe unittest
3169     {
3170         assertThrown!DateTimeException(DateTime.fromISOExtString(""));
3171         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
3172         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
3173         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
3174         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
3175         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
3176 
3177         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
3178         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3179         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3180         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
3181         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
3182         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
3183 
3184         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
3185         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
3186         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
3187         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
3188 
3189         assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
3190         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
3191 
3192         assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01)));
3193         assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3194         assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3195         assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3196         assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3197         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3198         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3199     }
3200 
3201     // bug# 17801
3202     @safe unittest
3203     {
3204         import std.conv : to;
3205         import std.meta : AliasSeq;
3206         foreach (C; AliasSeq!(char, wchar, dchar))
3207         {
3208             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3209                 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3210         }
3211     }
3212 
3213 
3214     /++
3215         Creates a $(LREF DateTime) from a string with the format
3216         YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
3217 
3218         Params:
3219             simpleString = A string formatted in the way that toSimpleString
3220                            formats dates and times.
3221 
3222         Throws:
3223             $(REF DateTimeException,std,datetime,date) if the given string is
3224             not in the correct format or if the resulting $(LREF DateTime)
3225             would not be valid.
3226       +/
3227     static DateTime fromSimpleString(S)(in S simpleString) @safe pure
3228         if (isSomeString!(S))
3229     {
3230         import std.algorithm.searching : countUntil;
3231         import std.exception : enforce;
3232         import std.format : format;
3233         import std.string : strip;
3234 
3235         auto str = strip(simpleString);
3236 
3237         enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
3238         auto t = str.countUntil(' ');
3239 
3240         enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
3241 
3242         immutable date = Date.fromSimpleString(str[0 .. t]);
3243         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3244 
3245         return DateTime(date, tod);
3246     }
3247 
3248     ///
3249     @safe unittest
3250     {
3251         assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
3252                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3253         assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
3254                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3255         assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
3256                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3257         assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
3258                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3259         assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
3260                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3261     }
3262 
3263     @safe unittest
3264     {
3265         assertThrown!DateTimeException(DateTime.fromISOString(""));
3266         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3267         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3268         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3269         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3270         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3271 
3272         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3273         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3274         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3275         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3276         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3277 
3278         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3279         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3280         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3281         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3282         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3283         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3284 
3285         assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
3286         assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
3287 
3288         assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
3289                DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01)));
3290         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
3291                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3292         assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
3293                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3294         assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
3295                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3296         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
3297                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3298         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
3299                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3300         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
3301                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3302     }
3303 
3304     // bug# 17801
3305     @safe unittest
3306     {
3307         import std.conv : to;
3308         import std.meta : AliasSeq;
3309         foreach (C; AliasSeq!(char, wchar, dchar))
3310         {
3311             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3312                 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3313         }
3314     }
3315 
3316 
3317     /++
3318         Returns the $(LREF DateTime) farthest in the past which is representable
3319         by $(LREF DateTime).
3320       +/
minDateTime3321     @property static DateTime min() @safe pure nothrow @nogc
3322     out(result)
3323     {
3324         assert(result._date == Date.min);
3325         assert(result._tod == TimeOfDay.min);
3326     }
3327     body
3328     {
3329         auto dt = DateTime.init;
3330         dt._date._year = short.min;
3331         dt._date._month = Month.jan;
3332         dt._date._day = 1;
3333 
3334         return dt;
3335     }
3336 
3337     @safe unittest
3338     {
3339         assert(DateTime.min.year < 0);
3340         assert(DateTime.min < DateTime.max);
3341     }
3342 
3343 
3344     /++
3345         Returns the $(LREF DateTime) farthest in the future which is
3346         representable by $(LREF DateTime).
3347       +/
maxDateTime3348     @property static DateTime max() @safe pure nothrow @nogc
3349     out(result)
3350     {
3351         assert(result._date == Date.max);
3352         assert(result._tod == TimeOfDay.max);
3353     }
3354     body
3355     {
3356         auto dt = DateTime.init;
3357         dt._date._year = short.max;
3358         dt._date._month = Month.dec;
3359         dt._date._day = 31;
3360         dt._tod._hour = TimeOfDay.maxHour;
3361         dt._tod._minute = TimeOfDay.maxMinute;
3362         dt._tod._second = TimeOfDay.maxSecond;
3363 
3364         return dt;
3365     }
3366 
3367     @safe unittest
3368     {
3369         assert(DateTime.max.year > 0);
3370         assert(DateTime.max > DateTime.min);
3371     }
3372 
3373 
3374 private:
3375 
3376     /+
3377         Add seconds to the time of day. Negative values will subtract. If the
3378         number of seconds overflows (or underflows), then the seconds will wrap,
3379         increasing (or decreasing) the number of minutes accordingly. The
3380         same goes for any larger units.
3381 
3382         Params:
3383             seconds = The number of seconds to add to this $(LREF DateTime).
3384       +/
3385     ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
3386     {
3387         long hnsecs = convert!("seconds", "hnsecs")(seconds);
3388         hnsecs += convert!("hours", "hnsecs")(_tod._hour);
3389         hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
3390         hnsecs += convert!("seconds", "hnsecs")(_tod._second);
3391 
3392         auto days = splitUnitsFromHNSecs!"days"(hnsecs);
3393 
3394         if (hnsecs < 0)
3395         {
3396             hnsecs += convert!("days", "hnsecs")(1);
3397             --days;
3398         }
3399 
3400         _date._addDays(days);
3401 
3402         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
3403         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
3404         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
3405 
3406         _tod._hour = cast(ubyte) newHours;
3407         _tod._minute = cast(ubyte) newMinutes;
3408         _tod._second = cast(ubyte) newSeconds;
3409 
3410         return this;
3411     }
3412 
3413     @safe unittest
3414     {
3415         static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__)
3416         {
3417             orig._addSeconds(seconds);
3418             assert(orig == expected);
3419         }
3420 
3421         // Test A.D.
3422         testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
3423         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
3424         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
3425         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
3426         testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
3427         testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
3428         testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
3429         testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
3430         testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
3431         testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
3432         testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
3433         testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
3434         testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
3435         testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
3436 
3437         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
3438         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
3439         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
3440         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
3441         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
3442         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
3443         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
3444         testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
3445         testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
3446 
3447         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
3448         testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
3449         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
3450         testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
3451         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
3452         testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
3453         testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
3454         testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
3455         testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
3456         testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
3457         testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
3458         testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
3459         testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
3460 
3461         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
3462         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
3463         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
3464         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
3465         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
3466         testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
3467 
3468         testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
3469         testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
3470         testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
3471 
3472         testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
3473         testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
3474         testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
3475 
3476         testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
3477         testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
3478         testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
3479 
3480         testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
3481         testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
3482         testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
3483 
3484         testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
3485         testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
3486         testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
3487 
3488         testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
3489         testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
3490         testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
3491 
3492         // Test B.C.
3493         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
3494         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
3495         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
3496         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
3497         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
3498         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
3499         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
3500         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
3501         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
3502         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
3503         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
3504         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
3505         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
3506         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
3507 
3508         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
3509         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
3510         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
3511         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
3512         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
3513         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
3514         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
3515         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
3516         testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
3517 
3518         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
3519         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
3520         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
3521         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
3522         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
3523         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
3524         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
3525         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
3526         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
3527         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
3528         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
3529         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
3530         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
3531 
3532         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
3533         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
3534         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
3535         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
3536         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
3537         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
3538         testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
3539 
3540         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
3541         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
3542         testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
3543 
3544         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
3545         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
3546         testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
3547 
3548         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
3549         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
3550         testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
3551 
3552         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
3553         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
3554         testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
3555 
3556         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
3557         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
3558         testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
3559 
3560         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
3561         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
3562         testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
3563 
3564         // Test Both
3565         testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
3566         testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
3567 
3568         testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
3569         testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
3570 
3571         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
3572         testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
3573 
3574         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
3575         testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
3576 
3577         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3578         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3579         static assert(!__traits(compiles, cdt._addSeconds(4)));
3580         static assert(!__traits(compiles, idt._addSeconds(4)));
3581     }
3582 
3583 
3584     Date      _date;
3585     TimeOfDay _tod;
3586 }
3587 
3588 
3589 /++
3590     Represents a date in the
3591     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
3592     Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
3593     are A.D. Non-positive years are B.C.
3594 
3595     Year, month, and day are kept separately internally so that $(D Date) is
3596     optimized for calendar-based operations.
3597 
3598     $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
3599     leap year calculations for its entire length. As per
3600     $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
3601     year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
3602     as a positive integer with 1 B.C. being the year prior to 1 A.D.
3603 
3604     Year 0 is a leap year.
3605  +/
3606 struct Date
3607 {
3608 public:
3609 
3610     /++
3611         Throws:
3612             $(REF DateTimeException,std,datetime,date) if the resulting
3613             $(LREF Date) would not be valid.
3614 
3615         Params:
3616             year  = Year of the Gregorian Calendar. Positive values are A.D.
3617                     Non-positive values are B.C. with year 0 being the year
3618                     prior to 1 A.D.
3619             month = Month of the year (January is 1).
3620             day   = Day of the month.
3621      +/
this(int year,int month,int day)3622     this(int year, int month, int day) @safe pure
3623     {
3624         enforceValid!"months"(cast(Month) month);
3625         enforceValid!"days"(year, cast(Month) month, day);
3626 
3627         _year  = cast(short) year;
3628         _month = cast(Month) month;
3629         _day   = cast(ubyte) day;
3630     }
3631 
3632     @safe unittest
3633     {
3634         import std.exception : assertNotThrown;
3635         assert(Date(1, 1, 1) == Date.init);
3636 
testDate(in Date date,int year,int month,int day)3637         static void testDate(in Date date, int year, int month, int day)
3638         {
3639             assert(date._year == year);
3640             assert(date._month == month);
3641             assert(date._day == day);
3642         }
3643 
3644         testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
3645         testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
3646         testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
3647 
3648         // Test A.D.
3649         assertThrown!DateTimeException(Date(1, 0, 1));
3650         assertThrown!DateTimeException(Date(1, 1, 0));
3651         assertThrown!DateTimeException(Date(1999, 13, 1));
3652         assertThrown!DateTimeException(Date(1999, 1, 32));
3653         assertThrown!DateTimeException(Date(1999, 2, 29));
3654         assertThrown!DateTimeException(Date(2000, 2, 30));
3655         assertThrown!DateTimeException(Date(1999, 3, 32));
3656         assertThrown!DateTimeException(Date(1999, 4, 31));
3657         assertThrown!DateTimeException(Date(1999, 5, 32));
3658         assertThrown!DateTimeException(Date(1999, 6, 31));
3659         assertThrown!DateTimeException(Date(1999, 7, 32));
3660         assertThrown!DateTimeException(Date(1999, 8, 32));
3661         assertThrown!DateTimeException(Date(1999, 9, 31));
3662         assertThrown!DateTimeException(Date(1999, 10, 32));
3663         assertThrown!DateTimeException(Date(1999, 11, 31));
3664         assertThrown!DateTimeException(Date(1999, 12, 32));
3665 
3666         assertNotThrown!DateTimeException(Date(1999, 1, 31));
3667         assertNotThrown!DateTimeException(Date(1999, 2, 28));
3668         assertNotThrown!DateTimeException(Date(2000, 2, 29));
3669         assertNotThrown!DateTimeException(Date(1999, 3, 31));
3670         assertNotThrown!DateTimeException(Date(1999, 4, 30));
3671         assertNotThrown!DateTimeException(Date(1999, 5, 31));
3672         assertNotThrown!DateTimeException(Date(1999, 6, 30));
3673         assertNotThrown!DateTimeException(Date(1999, 7, 31));
3674         assertNotThrown!DateTimeException(Date(1999, 8, 31));
3675         assertNotThrown!DateTimeException(Date(1999, 9, 30));
3676         assertNotThrown!DateTimeException(Date(1999, 10, 31));
3677         assertNotThrown!DateTimeException(Date(1999, 11, 30));
3678         assertNotThrown!DateTimeException(Date(1999, 12, 31));
3679 
3680         // Test B.C.
3681         assertNotThrown!DateTimeException(Date(0, 1, 1));
3682         assertNotThrown!DateTimeException(Date(-1, 1, 1));
3683         assertNotThrown!DateTimeException(Date(-1, 12, 31));
3684         assertNotThrown!DateTimeException(Date(-1, 2, 28));
3685         assertNotThrown!DateTimeException(Date(-4, 2, 29));
3686 
3687         assertThrown!DateTimeException(Date(-1, 2, 29));
3688         assertThrown!DateTimeException(Date(-2, 2, 29));
3689         assertThrown!DateTimeException(Date(-3, 2, 29));
3690     }
3691 
3692 
3693     /++
3694         Params:
3695             day = The Xth day of the Gregorian Calendar that the constructed
3696                   $(LREF Date) will be for.
3697      +/
this(int day)3698     this(int day) @safe pure nothrow @nogc
3699     {
3700         if (day > 0)
3701         {
3702             int years = (day / daysIn400Years) * 400 + 1;
3703             day %= daysIn400Years;
3704 
3705             {
3706                 immutable tempYears = day / daysIn100Years;
3707 
3708                 if (tempYears == 4)
3709                 {
3710                     years += 300;
3711                     day -= daysIn100Years * 3;
3712                 }
3713                 else
3714                 {
3715                     years += tempYears * 100;
3716                     day %= daysIn100Years;
3717                 }
3718             }
3719 
3720             years += (day / daysIn4Years) * 4;
3721             day %= daysIn4Years;
3722 
3723             {
3724                 immutable tempYears = day / daysInYear;
3725 
3726                 if (tempYears == 4)
3727                 {
3728                     years += 3;
3729                     day -= daysInYear * 3;
3730                 }
3731                 else
3732                 {
3733                     years += tempYears;
3734                     day %= daysInYear;
3735                 }
3736             }
3737 
3738             if (day == 0)
3739             {
3740                 _year = cast(short)(years - 1);
3741                 _month = Month.dec;
3742                 _day = 31;
3743             }
3744             else
3745             {
3746                 _year = cast(short) years;
3747 
3748                 setDayOfYear(day);
3749             }
3750         }
3751         else if (day <= 0 && -day < daysInLeapYear)
3752         {
3753             _year = 0;
3754 
3755             setDayOfYear(daysInLeapYear + day);
3756         }
3757         else
3758         {
3759             day += daysInLeapYear - 1;
3760             int years = (day / daysIn400Years) * 400 - 1;
3761             day %= daysIn400Years;
3762 
3763             {
3764                 immutable tempYears = day / daysIn100Years;
3765 
3766                 if (tempYears == -4)
3767                 {
3768                     years -= 300;
3769                     day += daysIn100Years * 3;
3770                 }
3771                 else
3772                 {
3773                     years += tempYears * 100;
3774                     day %= daysIn100Years;
3775                 }
3776             }
3777 
3778             years += (day / daysIn4Years) * 4;
3779             day %= daysIn4Years;
3780 
3781             {
3782                 immutable tempYears = day / daysInYear;
3783 
3784                 if (tempYears == -4)
3785                 {
3786                     years -= 3;
3787                     day += daysInYear * 3;
3788                 }
3789                 else
3790                 {
3791                     years += tempYears;
3792                     day %= daysInYear;
3793                 }
3794             }
3795 
3796             if (day == 0)
3797             {
3798                 _year = cast(short)(years + 1);
3799                 _month = Month.jan;
3800                 _day = 1;
3801             }
3802             else
3803             {
3804                 _year = cast(short) years;
3805                 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
3806 
3807                 setDayOfYear(newDoY);
3808             }
3809         }
3810     }
3811 
3812     @safe unittest
3813     {
3814         import std.range : chain;
3815 
3816         // Test A.D.
3817         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
3818             assert(Date(gd.day) == gd.date);
3819     }
3820 
3821 
3822     /++
3823         Compares this $(LREF Date) with the given $(LREF Date).
3824 
3825         Returns:
3826             $(BOOKTABLE,
3827             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
3828             $(TR $(TD this == rhs) $(TD 0))
3829             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
3830             )
3831      +/
opCmp(in Date rhs)3832     int opCmp(in Date rhs) const @safe pure nothrow @nogc
3833     {
3834         if (_year < rhs._year)
3835             return -1;
3836         if (_year > rhs._year)
3837             return 1;
3838 
3839         if (_month < rhs._month)
3840             return -1;
3841         if (_month > rhs._month)
3842             return 1;
3843 
3844         if (_day < rhs._day)
3845             return -1;
3846         if (_day > rhs._day)
3847             return 1;
3848 
3849         return 0;
3850     }
3851 
3852     @safe unittest
3853     {
3854         // Test A.D.
3855         assert(Date(1, 1, 1).opCmp(Date.init) == 0);
3856 
3857         assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
3858         assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
3859         assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
3860 
3861         assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
3862         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
3863 
3864         assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
3865 
3866         assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
3867         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
3868         assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
3869         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
3870         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
3871         assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
3872 
3873         assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
3874         assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
3875         assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
3876         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
3877         assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
3878         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
3879 
3880         // Test B.C.
3881         assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
3882         assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
3883         assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
3884         assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
3885 
3886         assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
3887         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
3888 
3889         assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
3890 
3891         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
3892         assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
3893         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
3894         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
3895         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
3896         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
3897 
3898         assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
3899         assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
3900         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
3901         assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
3902         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
3903         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
3904 
3905         // Test Both
3906         assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
3907         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
3908 
3909         assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
3910         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
3911 
3912         assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
3913         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
3914 
3915         assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
3916         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
3917 
3918         assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
3919         assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
3920 
3921         auto date = Date(1999, 7, 6);
3922         const cdate = Date(1999, 7, 6);
3923         immutable idate = Date(1999, 7, 6);
3924         assert(date.opCmp(date) == 0);
3925         assert(date.opCmp(cdate) == 0);
3926         assert(date.opCmp(idate) == 0);
3927         assert(cdate.opCmp(date) == 0);
3928         assert(cdate.opCmp(cdate) == 0);
3929         assert(cdate.opCmp(idate) == 0);
3930         assert(idate.opCmp(date) == 0);
3931         assert(idate.opCmp(cdate) == 0);
3932         assert(idate.opCmp(idate) == 0);
3933     }
3934 
3935 
3936     /++
3937         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
3938         are B.C.
3939      +/
year()3940     @property short year() const @safe pure nothrow @nogc
3941     {
3942         return _year;
3943     }
3944 
3945     ///
3946     @safe unittest
3947     {
3948         assert(Date(1999, 7, 6).year == 1999);
3949         assert(Date(2010, 10, 4).year == 2010);
3950         assert(Date(-7, 4, 5).year == -7);
3951     }
3952 
3953     @safe unittest
3954     {
3955         assert(Date.init.year == 1);
3956         assert(Date(1999, 7, 6).year == 1999);
3957         assert(Date(-1999, 7, 6).year == -1999);
3958 
3959         const cdate = Date(1999, 7, 6);
3960         immutable idate = Date(1999, 7, 6);
3961         assert(cdate.year == 1999);
3962         assert(idate.year == 1999);
3963     }
3964 
3965     /++
3966         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
3967         are B.C.
3968 
3969         Params:
3970             year = The year to set this Date's year to.
3971 
3972         Throws:
3973             $(REF DateTimeException,std,datetime,date) if the new year is not
3974             a leap year and the resulting date would be on February 29th.
3975      +/
year(int year)3976     @property void year(int year) @safe pure
3977     {
3978         enforceValid!"days"(year, _month, _day);
3979         _year = cast(short) year;
3980     }
3981 
3982     ///
3983     @safe unittest
3984     {
3985         assert(Date(1999, 7, 6).year == 1999);
3986         assert(Date(2010, 10, 4).year == 2010);
3987         assert(Date(-7, 4, 5).year == -7);
3988     }
3989 
3990     @safe unittest
3991     {
3992         static void testDateInvalid(Date date, int year)
3993         {
3994             date.year = year;
3995         }
3996 
3997         static void testDate(Date date, int year, in Date expected)
3998         {
3999             date.year = year;
4000             assert(date == expected);
4001         }
4002 
4003         assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
4004 
4005         testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
4006         testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
4007         testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
4008 
4009         const cdate = Date(1999, 7, 6);
4010         immutable idate = Date(1999, 7, 6);
4011         static assert(!__traits(compiles, cdate.year = 1999));
4012         static assert(!__traits(compiles, idate.year = 1999));
4013     }
4014 
4015 
4016     /++
4017         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4018 
4019         Throws:
4020             $(REF DateTimeException,std,datetime,date) if $(D isAD) is true.
4021      +/
4022     @property ushort yearBC() const @safe pure
4023     {
4024         import std.format : format;
4025 
4026         if (isAD)
4027             throw new DateTimeException(format("Year %s is A.D.", _year));
4028         return cast(ushort)((_year * -1) + 1);
4029     }
4030 
4031     ///
4032     @safe unittest
4033     {
4034         assert(Date(0, 1, 1).yearBC == 1);
4035         assert(Date(-1, 1, 1).yearBC == 2);
4036         assert(Date(-100, 1, 1).yearBC == 101);
4037     }
4038 
4039     @safe unittest
4040     {
4041         assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1)));
4042 
4043         auto date = Date(0, 7, 6);
4044         const cdate = Date(0, 7, 6);
4045         immutable idate = Date(0, 7, 6);
4046         assert(date.yearBC == 1);
4047         assert(cdate.yearBC == 1);
4048         assert(idate.yearBC == 1);
4049     }
4050 
4051 
4052     /++
4053         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4054 
4055         Params:
4056             year = The year B.C. to set this $(LREF Date)'s year to.
4057 
4058         Throws:
4059             $(REF DateTimeException,std,datetime,date) if a non-positive value
4060             is given.
4061      +/
4062     @property void yearBC(int year) @safe pure
4063     {
4064         if (year <= 0)
4065             throw new DateTimeException("The given year is not a year B.C.");
4066         _year = cast(short)((year - 1) * -1);
4067     }
4068 
4069     ///
4070     @safe unittest
4071     {
4072         auto date = Date(2010, 1, 1);
4073         date.yearBC = 1;
4074         assert(date == Date(0, 1, 1));
4075 
4076         date.yearBC = 10;
4077         assert(date == Date(-9, 1, 1));
4078     }
4079 
4080     @safe unittest
4081     {
4082         assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
4083 
4084         auto date = Date(0, 7, 6);
4085         const cdate = Date(0, 7, 6);
4086         immutable idate = Date(0, 7, 6);
4087         date.yearBC = 7;
4088         assert(date.yearBC == 7);
4089         static assert(!__traits(compiles, cdate.yearBC = 7));
4090         static assert(!__traits(compiles, idate.yearBC = 7));
4091     }
4092 
4093 
4094     /++
4095         Month of a Gregorian Year.
4096      +/
month()4097     @property Month month() const @safe pure nothrow @nogc
4098     {
4099         return _month;
4100     }
4101 
4102     ///
4103     @safe unittest
4104     {
4105         assert(Date(1999, 7, 6).month == 7);
4106         assert(Date(2010, 10, 4).month == 10);
4107         assert(Date(-7, 4, 5).month == 4);
4108     }
4109 
4110     @safe unittest
4111     {
4112         assert(Date.init.month == 1);
4113         assert(Date(1999, 7, 6).month == 7);
4114         assert(Date(-1999, 7, 6).month == 7);
4115 
4116         const cdate = Date(1999, 7, 6);
4117         immutable idate = Date(1999, 7, 6);
4118         assert(cdate.month == 7);
4119         assert(idate.month == 7);
4120     }
4121 
4122     /++
4123         Month of a Gregorian Year.
4124 
4125         Params:
4126             month = The month to set this $(LREF Date)'s month to.
4127 
4128         Throws:
4129             $(REF DateTimeException,std,datetime,date) if the given month is
4130             not a valid month or if the current day would not be valid in the
4131             given month.
4132      +/
month(Month month)4133     @property void month(Month month) @safe pure
4134     {
4135         enforceValid!"months"(month);
4136         enforceValid!"days"(_year, month, _day);
4137         _month = cast(Month) month;
4138     }
4139 
4140     @safe unittest
4141     {
4142         static void testDate(Date date, Month month, in Date expected = Date.init)
4143         {
4144             date.month = month;
4145             assert(expected != Date.init);
4146             assert(date == expected);
4147         }
4148 
4149         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
4150         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
4151         assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
4152         assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
4153 
4154         testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
4155         testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
4156 
4157         const cdate = Date(1999, 7, 6);
4158         immutable idate = Date(1999, 7, 6);
4159         static assert(!__traits(compiles, cdate.month = 7));
4160         static assert(!__traits(compiles, idate.month = 7));
4161     }
4162 
4163 
4164     /++
4165         Day of a Gregorian Month.
4166      +/
4167     @property ubyte day() const @safe pure nothrow @nogc
4168     {
4169         return _day;
4170     }
4171 
4172     ///
4173     @safe unittest
4174     {
4175         assert(Date(1999, 7, 6).day == 6);
4176         assert(Date(2010, 10, 4).day == 4);
4177         assert(Date(-7, 4, 5).day == 5);
4178     }
4179 
4180     @safe unittest
4181     {
4182         import std.format : format;
4183         import std.range : chain;
4184 
4185         static void test(Date date, int expected)
4186         {
4187             assert(date.day == expected, format("Value given: %s", date));
4188         }
4189 
4190         foreach (year; chain(testYearsBC, testYearsAD))
4191         {
4192             foreach (md; testMonthDays)
4193                 test(Date(year, md.month, md.day), md.day);
4194         }
4195 
4196         const cdate = Date(1999, 7, 6);
4197         immutable idate = Date(1999, 7, 6);
4198         assert(cdate.day == 6);
4199         assert(idate.day == 6);
4200     }
4201 
4202     /++
4203         Day of a Gregorian Month.
4204 
4205         Params:
4206             day = The day of the month to set this $(LREF Date)'s day to.
4207 
4208         Throws:
4209             $(REF DateTimeException,std,datetime,date) if the given day is not
4210             a valid day of the current month.
4211      +/
4212     @property void day(int day) @safe pure
4213     {
4214         enforceValid!"days"(_year, _month, day);
4215         _day = cast(ubyte) day;
4216     }
4217 
4218     @safe unittest
4219     {
4220         import std.exception : assertNotThrown;
4221 
testDate(Date date,int day)4222         static void testDate(Date date, int day)
4223         {
4224             date.day = day;
4225         }
4226 
4227         // Test A.D.
4228         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
4229         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
4230         assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
4231         assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
4232         assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
4233         assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
4234         assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
4235         assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
4236         assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
4237         assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
4238         assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
4239         assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
4240         assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
4241         assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
4242 
4243         assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
4244         assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
4245         assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
4246         assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
4247         assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
4248         assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
4249         assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
4250         assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
4251         assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
4252         assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
4253         assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
4254         assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
4255         assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
4256 
4257         {
4258             auto date = Date(1, 1, 1);
4259             date.day = 6;
4260             assert(date == Date(1, 1, 6));
4261         }
4262 
4263         // Test B.C.
4264         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
4265         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
4266         assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
4267         assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
4268         assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
4269         assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
4270         assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
4271         assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
4272         assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
4273         assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
4274         assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
4275         assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
4276         assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
4277         assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
4278 
4279         assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
4280         assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
4281         assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
4282         assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
4283         assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
4284         assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
4285         assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
4286         assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
4287         assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
4288         assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
4289         assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
4290         assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
4291         assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
4292 
4293         {
4294             auto date = Date(-1, 1, 1);
4295             date.day = 6;
4296             assert(date == Date(-1, 1, 6));
4297         }
4298 
4299         const cdate = Date(1999, 7, 6);
4300         immutable idate = Date(1999, 7, 6);
4301         static assert(!__traits(compiles, cdate.day = 6));
4302         static assert(!__traits(compiles, idate.day = 6));
4303     }
4304 
4305 
4306     /++
4307         Adds the given number of years or months to this $(LREF Date). A
4308         negative number will subtract.
4309 
4310         Note that if day overflow is allowed, and the date with the adjusted
4311         year/month overflows the number of days in the new month, then the month
4312         will be incremented by one, and the day set to the number of days
4313         overflowed. (e.g. if the day were 31 and the new month were June, then
4314         the month would be incremented to July, and the new day would be 1). If
4315         day overflow is not allowed, then the day will be set to the last valid
4316         day in the month (e.g. June 31st would become June 30th).
4317 
4318         Params:
4319             units         = The type of units to add ("years" or "months").
4320             value         = The number of months or years to add to this
4321                             $(LREF Date).
4322             allowOverflow = Whether the day should be allowed to overflow,
4323                             causing the month to increment.
4324       +/
4325     @safe pure nothrow @nogc
4326     ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4327         if (units == "years")
4328     {
4329         _year += value;
4330 
4331         if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
4332         {
4333             if (allowOverflow == AllowDayOverflow.yes)
4334             {
4335                 _month = Month.mar;
4336                 _day = 1;
4337             }
4338             else
4339                 _day = 28;
4340         }
4341 
4342         return this;
4343     }
4344 
4345     ///
4346     @safe unittest
4347     {
4348         auto d1 = Date(2010, 1, 1);
4349         d1.add!"months"(11);
4350         assert(d1 == Date(2010, 12, 1));
4351 
4352         auto d2 = Date(2010, 1, 1);
4353         d2.add!"months"(-11);
4354         assert(d2 == Date(2009, 2, 1));
4355 
4356         auto d3 = Date(2000, 2, 29);
4357         d3.add!"years"(1);
4358         assert(d3 == Date(2001, 3, 1));
4359 
4360         auto d4 = Date(2000, 2, 29);
4361         d4.add!"years"(1, AllowDayOverflow.no);
4362         assert(d4 == Date(2001, 2, 28));
4363     }
4364 
4365     // Test add!"years"() with AllowDayOverflow.yes
4366     @safe unittest
4367     {
4368         // Test A.D.
4369         {
4370             auto date = Date(1999, 7, 6);
4371             date.add!"years"(7);
4372             assert(date == Date(2006, 7, 6));
4373             date.add!"years"(-9);
4374             assert(date == Date(1997, 7, 6));
4375         }
4376 
4377         {
4378             auto date = Date(1999, 2, 28);
4379             date.add!"years"(1);
4380             assert(date == Date(2000, 2, 28));
4381         }
4382 
4383         {
4384             auto date = Date(2000, 2, 29);
4385             date.add!"years"(-1);
4386             assert(date == Date(1999, 3, 1));
4387         }
4388 
4389         // Test B.C.
4390         {
4391             auto date = Date(-1999, 7, 6);
4392             date.add!"years"(-7);
4393             assert(date == Date(-2006, 7, 6));
4394             date.add!"years"(9);
4395             assert(date == Date(-1997, 7, 6));
4396         }
4397 
4398         {
4399             auto date = Date(-1999, 2, 28);
4400             date.add!"years"(-1);
4401             assert(date == Date(-2000, 2, 28));
4402         }
4403 
4404         {
4405             auto date = Date(-2000, 2, 29);
4406             date.add!"years"(1);
4407             assert(date == Date(-1999, 3, 1));
4408         }
4409 
4410         // Test Both
4411         {
4412             auto date = Date(4, 7, 6);
4413             date.add!"years"(-5);
4414             assert(date == Date(-1, 7, 6));
4415             date.add!"years"(5);
4416             assert(date == Date(4, 7, 6));
4417         }
4418 
4419         {
4420             auto date = Date(-4, 7, 6);
4421             date.add!"years"(5);
4422             assert(date == Date(1, 7, 6));
4423             date.add!"years"(-5);
4424             assert(date == Date(-4, 7, 6));
4425         }
4426 
4427         {
4428             auto date = Date(4, 7, 6);
4429             date.add!"years"(-8);
4430             assert(date == Date(-4, 7, 6));
4431             date.add!"years"(8);
4432             assert(date == Date(4, 7, 6));
4433         }
4434 
4435         {
4436             auto date = Date(-4, 7, 6);
4437             date.add!"years"(8);
4438             assert(date == Date(4, 7, 6));
4439             date.add!"years"(-8);
4440             assert(date == Date(-4, 7, 6));
4441         }
4442 
4443         {
4444             auto date = Date(-4, 2, 29);
4445             date.add!"years"(5);
4446             assert(date == Date(1, 3, 1));
4447         }
4448 
4449         {
4450             auto date = Date(4, 2, 29);
4451             date.add!"years"(-5);
4452             assert(date == Date(-1, 3, 1));
4453         }
4454 
4455         {
4456             auto date = Date(4, 2, 29);
4457             date.add!"years"(-5).add!"years"(7);
4458             assert(date == Date(6, 3, 1));
4459         }
4460 
4461         const cdate = Date(1999, 7, 6);
4462         immutable idate = Date(1999, 7, 6);
4463         static assert(!__traits(compiles, cdate.add!"years"(7)));
4464         static assert(!__traits(compiles, idate.add!"years"(7)));
4465     }
4466 
4467     // Test add!"years"() with AllowDayOverflow.no
4468     @safe unittest
4469     {
4470         // Test A.D.
4471         {
4472             auto date = Date(1999, 7, 6);
4473             date.add!"years"(7, AllowDayOverflow.no);
4474             assert(date == Date(2006, 7, 6));
4475             date.add!"years"(-9, AllowDayOverflow.no);
4476             assert(date == Date(1997, 7, 6));
4477         }
4478 
4479         {
4480             auto date = Date(1999, 2, 28);
4481             date.add!"years"(1, AllowDayOverflow.no);
4482             assert(date == Date(2000, 2, 28));
4483         }
4484 
4485         {
4486             auto date = Date(2000, 2, 29);
4487             date.add!"years"(-1, AllowDayOverflow.no);
4488             assert(date == Date(1999, 2, 28));
4489         }
4490 
4491         // Test B.C.
4492         {
4493             auto date = Date(-1999, 7, 6);
4494             date.add!"years"(-7, AllowDayOverflow.no);
4495             assert(date == Date(-2006, 7, 6));
4496             date.add!"years"(9, AllowDayOverflow.no);
4497             assert(date == Date(-1997, 7, 6));
4498         }
4499 
4500         {
4501             auto date = Date(-1999, 2, 28);
4502             date.add!"years"(-1, AllowDayOverflow.no);
4503             assert(date == Date(-2000, 2, 28));
4504         }
4505 
4506         {
4507             auto date = Date(-2000, 2, 29);
4508             date.add!"years"(1, AllowDayOverflow.no);
4509             assert(date == Date(-1999, 2, 28));
4510         }
4511 
4512         // Test Both
4513         {
4514             auto date = Date(4, 7, 6);
4515             date.add!"years"(-5, AllowDayOverflow.no);
4516             assert(date == Date(-1, 7, 6));
4517             date.add!"years"(5, AllowDayOverflow.no);
4518             assert(date == Date(4, 7, 6));
4519         }
4520 
4521         {
4522             auto date = Date(-4, 7, 6);
4523             date.add!"years"(5, AllowDayOverflow.no);
4524             assert(date == Date(1, 7, 6));
4525             date.add!"years"(-5, AllowDayOverflow.no);
4526             assert(date == Date(-4, 7, 6));
4527         }
4528 
4529         {
4530             auto date = Date(4, 7, 6);
4531             date.add!"years"(-8, AllowDayOverflow.no);
4532             assert(date == Date(-4, 7, 6));
4533             date.add!"years"(8, AllowDayOverflow.no);
4534             assert(date == Date(4, 7, 6));
4535         }
4536 
4537         {
4538             auto date = Date(-4, 7, 6);
4539             date.add!"years"(8, AllowDayOverflow.no);
4540             assert(date == Date(4, 7, 6));
4541             date.add!"years"(-8, AllowDayOverflow.no);
4542             assert(date == Date(-4, 7, 6));
4543         }
4544 
4545         {
4546             auto date = Date(-4, 2, 29);
4547             date.add!"years"(5, AllowDayOverflow.no);
4548             assert(date == Date(1, 2, 28));
4549         }
4550 
4551         {
4552             auto date = Date(4, 2, 29);
4553             date.add!"years"(-5, AllowDayOverflow.no);
4554             assert(date == Date(-1, 2, 28));
4555         }
4556 
4557         {
4558             auto date = Date(4, 2, 29);
4559             date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
4560             assert(date == Date(6, 2, 28));
4561         }
4562     }
4563 
4564 
4565     // Shares documentation with "years" version.
4566     @safe pure nothrow @nogc
4567     ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4568         if (units == "months")
4569     {
4570         auto years = months / 12;
4571         months %= 12;
4572         auto newMonth = _month + months;
4573 
4574         if (months < 0)
4575         {
4576             if (newMonth < 1)
4577             {
4578                 newMonth += 12;
4579                 --years;
4580             }
4581         }
4582         else if (newMonth > 12)
4583         {
4584             newMonth -= 12;
4585             ++years;
4586         }
4587 
4588         _year += years;
4589         _month = cast(Month) newMonth;
4590 
4591         immutable currMaxDay = maxDay(_year, _month);
4592         immutable overflow = _day - currMaxDay;
4593 
4594         if (overflow > 0)
4595         {
4596             if (allowOverflow == AllowDayOverflow.yes)
4597             {
4598                 ++_month;
4599                 _day = cast(ubyte) overflow;
4600             }
4601             else
4602                 _day = cast(ubyte) currMaxDay;
4603         }
4604 
4605         return this;
4606     }
4607 
4608     // Test add!"months"() with AllowDayOverflow.yes
4609     @safe unittest
4610     {
4611         // Test A.D.
4612         {
4613             auto date = Date(1999, 7, 6);
4614             date.add!"months"(3);
4615             assert(date == Date(1999, 10, 6));
4616             date.add!"months"(-4);
4617             assert(date == Date(1999, 6, 6));
4618         }
4619 
4620         {
4621             auto date = Date(1999, 7, 6);
4622             date.add!"months"(6);
4623             assert(date == Date(2000, 1, 6));
4624             date.add!"months"(-6);
4625             assert(date == Date(1999, 7, 6));
4626         }
4627 
4628         {
4629             auto date = Date(1999, 7, 6);
4630             date.add!"months"(27);
4631             assert(date == Date(2001, 10, 6));
4632             date.add!"months"(-28);
4633             assert(date == Date(1999, 6, 6));
4634         }
4635 
4636         {
4637             auto date = Date(1999, 5, 31);
4638             date.add!"months"(1);
4639             assert(date == Date(1999, 7, 1));
4640         }
4641 
4642         {
4643             auto date = Date(1999, 5, 31);
4644             date.add!"months"(-1);
4645             assert(date == Date(1999, 5, 1));
4646         }
4647 
4648         {
4649             auto date = Date(1999, 2, 28);
4650             date.add!"months"(12);
4651             assert(date == Date(2000, 2, 28));
4652         }
4653 
4654         {
4655             auto date = Date(2000, 2, 29);
4656             date.add!"months"(12);
4657             assert(date == Date(2001, 3, 1));
4658         }
4659 
4660         {
4661             auto date = Date(1999, 7, 31);
4662             date.add!"months"(1);
4663             assert(date == Date(1999, 8, 31));
4664             date.add!"months"(1);
4665             assert(date == Date(1999, 10, 1));
4666         }
4667 
4668         {
4669             auto date = Date(1998, 8, 31);
4670             date.add!"months"(13);
4671             assert(date == Date(1999, 10, 1));
4672             date.add!"months"(-13);
4673             assert(date == Date(1998, 9, 1));
4674         }
4675 
4676         {
4677             auto date = Date(1997, 12, 31);
4678             date.add!"months"(13);
4679             assert(date == Date(1999, 1, 31));
4680             date.add!"months"(-13);
4681             assert(date == Date(1997, 12, 31));
4682         }
4683 
4684         {
4685             auto date = Date(1997, 12, 31);
4686             date.add!"months"(14);
4687             assert(date == Date(1999, 3, 3));
4688             date.add!"months"(-14);
4689             assert(date == Date(1998, 1, 3));
4690         }
4691 
4692         {
4693             auto date = Date(1998, 12, 31);
4694             date.add!"months"(14);
4695             assert(date == Date(2000, 3, 2));
4696             date.add!"months"(-14);
4697             assert(date == Date(1999, 1, 2));
4698         }
4699 
4700         {
4701             auto date = Date(1999, 12, 31);
4702             date.add!"months"(14);
4703             assert(date == Date(2001, 3, 3));
4704             date.add!"months"(-14);
4705             assert(date == Date(2000, 1, 3));
4706         }
4707 
4708         // Test B.C.
4709         {
4710             auto date = Date(-1999, 7, 6);
4711             date.add!"months"(3);
4712             assert(date == Date(-1999, 10, 6));
4713             date.add!"months"(-4);
4714             assert(date == Date(-1999, 6, 6));
4715         }
4716 
4717         {
4718             auto date = Date(-1999, 7, 6);
4719             date.add!"months"(6);
4720             assert(date == Date(-1998, 1, 6));
4721             date.add!"months"(-6);
4722             assert(date == Date(-1999, 7, 6));
4723         }
4724 
4725         {
4726             auto date = Date(-1999, 7, 6);
4727             date.add!"months"(-27);
4728             assert(date == Date(-2001, 4, 6));
4729             date.add!"months"(28);
4730             assert(date == Date(-1999, 8, 6));
4731         }
4732 
4733         {
4734             auto date = Date(-1999, 5, 31);
4735             date.add!"months"(1);
4736             assert(date == Date(-1999, 7, 1));
4737         }
4738 
4739         {
4740             auto date = Date(-1999, 5, 31);
4741             date.add!"months"(-1);
4742             assert(date == Date(-1999, 5, 1));
4743         }
4744 
4745         {
4746             auto date = Date(-1999, 2, 28);
4747             date.add!"months"(-12);
4748             assert(date == Date(-2000, 2, 28));
4749         }
4750 
4751         {
4752             auto date = Date(-2000, 2, 29);
4753             date.add!"months"(-12);
4754             assert(date == Date(-2001, 3, 1));
4755         }
4756 
4757         {
4758             auto date = Date(-1999, 7, 31);
4759             date.add!"months"(1);
4760             assert(date == Date(-1999, 8, 31));
4761             date.add!"months"(1);
4762             assert(date == Date(-1999, 10, 1));
4763         }
4764 
4765         {
4766             auto date = Date(-1998, 8, 31);
4767             date.add!"months"(13);
4768             assert(date == Date(-1997, 10, 1));
4769             date.add!"months"(-13);
4770             assert(date == Date(-1998, 9, 1));
4771         }
4772 
4773         {
4774             auto date = Date(-1997, 12, 31);
4775             date.add!"months"(13);
4776             assert(date == Date(-1995, 1, 31));
4777             date.add!"months"(-13);
4778             assert(date == Date(-1997, 12, 31));
4779         }
4780 
4781         {
4782             auto date = Date(-1997, 12, 31);
4783             date.add!"months"(14);
4784             assert(date == Date(-1995, 3, 3));
4785             date.add!"months"(-14);
4786             assert(date == Date(-1996, 1, 3));
4787         }
4788 
4789         {
4790             auto date = Date(-2002, 12, 31);
4791             date.add!"months"(14);
4792             assert(date == Date(-2000, 3, 2));
4793             date.add!"months"(-14);
4794             assert(date == Date(-2001, 1, 2));
4795         }
4796 
4797         {
4798             auto date = Date(-2001, 12, 31);
4799             date.add!"months"(14);
4800             assert(date == Date(-1999, 3, 3));
4801             date.add!"months"(-14);
4802             assert(date == Date(-2000, 1, 3));
4803         }
4804 
4805         // Test Both
4806         {
4807             auto date = Date(1, 1, 1);
4808             date.add!"months"(-1);
4809             assert(date == Date(0, 12, 1));
4810             date.add!"months"(1);
4811             assert(date == Date(1, 1, 1));
4812         }
4813 
4814         {
4815             auto date = Date(4, 1, 1);
4816             date.add!"months"(-48);
4817             assert(date == Date(0, 1, 1));
4818             date.add!"months"(48);
4819             assert(date == Date(4, 1, 1));
4820         }
4821 
4822         {
4823             auto date = Date(4, 3, 31);
4824             date.add!"months"(-49);
4825             assert(date == Date(0, 3, 2));
4826             date.add!"months"(49);
4827             assert(date == Date(4, 4, 2));
4828         }
4829 
4830         {
4831             auto date = Date(4, 3, 31);
4832             date.add!"months"(-85);
4833             assert(date == Date(-3, 3, 3));
4834             date.add!"months"(85);
4835             assert(date == Date(4, 4, 3));
4836         }
4837 
4838         {
4839             auto date = Date(-3, 3, 31);
4840             date.add!"months"(85).add!"months"(-83);
4841             assert(date == Date(-3, 6, 1));
4842         }
4843 
4844         const cdate = Date(1999, 7, 6);
4845         immutable idate = Date(1999, 7, 6);
4846         static assert(!__traits(compiles, cdate.add!"months"(3)));
4847         static assert(!__traits(compiles, idate.add!"months"(3)));
4848     }
4849 
4850     // Test add!"months"() with AllowDayOverflow.no
4851     @safe unittest
4852     {
4853         // Test A.D.
4854         {
4855             auto date = Date(1999, 7, 6);
4856             date.add!"months"(3, AllowDayOverflow.no);
4857             assert(date == Date(1999, 10, 6));
4858             date.add!"months"(-4, AllowDayOverflow.no);
4859             assert(date == Date(1999, 6, 6));
4860         }
4861 
4862         {
4863             auto date = Date(1999, 7, 6);
4864             date.add!"months"(6, AllowDayOverflow.no);
4865             assert(date == Date(2000, 1, 6));
4866             date.add!"months"(-6, AllowDayOverflow.no);
4867             assert(date == Date(1999, 7, 6));
4868         }
4869 
4870         {
4871             auto date = Date(1999, 7, 6);
4872             date.add!"months"(27, AllowDayOverflow.no);
4873             assert(date == Date(2001, 10, 6));
4874             date.add!"months"(-28, AllowDayOverflow.no);
4875             assert(date == Date(1999, 6, 6));
4876         }
4877 
4878         {
4879             auto date = Date(1999, 5, 31);
4880             date.add!"months"(1, AllowDayOverflow.no);
4881             assert(date == Date(1999, 6, 30));
4882         }
4883 
4884         {
4885             auto date = Date(1999, 5, 31);
4886             date.add!"months"(-1, AllowDayOverflow.no);
4887             assert(date == Date(1999, 4, 30));
4888         }
4889 
4890         {
4891             auto date = Date(1999, 2, 28);
4892             date.add!"months"(12, AllowDayOverflow.no);
4893             assert(date == Date(2000, 2, 28));
4894         }
4895 
4896         {
4897             auto date = Date(2000, 2, 29);
4898             date.add!"months"(12, AllowDayOverflow.no);
4899             assert(date == Date(2001, 2, 28));
4900         }
4901 
4902         {
4903             auto date = Date(1999, 7, 31);
4904             date.add!"months"(1, AllowDayOverflow.no);
4905             assert(date == Date(1999, 8, 31));
4906             date.add!"months"(1, AllowDayOverflow.no);
4907             assert(date == Date(1999, 9, 30));
4908         }
4909 
4910         {
4911             auto date = Date(1998, 8, 31);
4912             date.add!"months"(13, AllowDayOverflow.no);
4913             assert(date == Date(1999, 9, 30));
4914             date.add!"months"(-13, AllowDayOverflow.no);
4915             assert(date == Date(1998, 8, 30));
4916         }
4917 
4918         {
4919             auto date = Date(1997, 12, 31);
4920             date.add!"months"(13, AllowDayOverflow.no);
4921             assert(date == Date(1999, 1, 31));
4922             date.add!"months"(-13, AllowDayOverflow.no);
4923             assert(date == Date(1997, 12, 31));
4924         }
4925 
4926         {
4927             auto date = Date(1997, 12, 31);
4928             date.add!"months"(14, AllowDayOverflow.no);
4929             assert(date == Date(1999, 2, 28));
4930             date.add!"months"(-14, AllowDayOverflow.no);
4931             assert(date == Date(1997, 12, 28));
4932         }
4933 
4934         {
4935             auto date = Date(1998, 12, 31);
4936             date.add!"months"(14, AllowDayOverflow.no);
4937             assert(date == Date(2000, 2, 29));
4938             date.add!"months"(-14, AllowDayOverflow.no);
4939             assert(date == Date(1998, 12, 29));
4940         }
4941 
4942         {
4943             auto date = Date(1999, 12, 31);
4944             date.add!"months"(14, AllowDayOverflow.no);
4945             assert(date == Date(2001, 2, 28));
4946             date.add!"months"(-14, AllowDayOverflow.no);
4947             assert(date == Date(1999, 12, 28));
4948         }
4949 
4950         // Test B.C.
4951         {
4952             auto date = Date(-1999, 7, 6);
4953             date.add!"months"(3, AllowDayOverflow.no);
4954             assert(date == Date(-1999, 10, 6));
4955             date.add!"months"(-4, AllowDayOverflow.no);
4956             assert(date == Date(-1999, 6, 6));
4957         }
4958 
4959         {
4960             auto date = Date(-1999, 7, 6);
4961             date.add!"months"(6, AllowDayOverflow.no);
4962             assert(date == Date(-1998, 1, 6));
4963             date.add!"months"(-6, AllowDayOverflow.no);
4964             assert(date == Date(-1999, 7, 6));
4965         }
4966 
4967         {
4968             auto date = Date(-1999, 7, 6);
4969             date.add!"months"(-27, AllowDayOverflow.no);
4970             assert(date == Date(-2001, 4, 6));
4971             date.add!"months"(28, AllowDayOverflow.no);
4972             assert(date == Date(-1999, 8, 6));
4973         }
4974 
4975         {
4976             auto date = Date(-1999, 5, 31);
4977             date.add!"months"(1, AllowDayOverflow.no);
4978             assert(date == Date(-1999, 6, 30));
4979         }
4980 
4981         {
4982             auto date = Date(-1999, 5, 31);
4983             date.add!"months"(-1, AllowDayOverflow.no);
4984             assert(date == Date(-1999, 4, 30));
4985         }
4986 
4987         {
4988             auto date = Date(-1999, 2, 28);
4989             date.add!"months"(-12, AllowDayOverflow.no);
4990             assert(date == Date(-2000, 2, 28));
4991         }
4992 
4993         {
4994             auto date = Date(-2000, 2, 29);
4995             date.add!"months"(-12, AllowDayOverflow.no);
4996             assert(date == Date(-2001, 2, 28));
4997         }
4998 
4999         {
5000             auto date = Date(-1999, 7, 31);
5001             date.add!"months"(1, AllowDayOverflow.no);
5002             assert(date == Date(-1999, 8, 31));
5003             date.add!"months"(1, AllowDayOverflow.no);
5004             assert(date == Date(-1999, 9, 30));
5005         }
5006 
5007         {
5008             auto date = Date(-1998, 8, 31);
5009             date.add!"months"(13, AllowDayOverflow.no);
5010             assert(date == Date(-1997, 9, 30));
5011             date.add!"months"(-13, AllowDayOverflow.no);
5012             assert(date == Date(-1998, 8, 30));
5013         }
5014 
5015         {
5016             auto date = Date(-1997, 12, 31);
5017             date.add!"months"(13, AllowDayOverflow.no);
5018             assert(date == Date(-1995, 1, 31));
5019             date.add!"months"(-13, AllowDayOverflow.no);
5020             assert(date == Date(-1997, 12, 31));
5021         }
5022 
5023         {
5024             auto date = Date(-1997, 12, 31);
5025             date.add!"months"(14, AllowDayOverflow.no);
5026             assert(date == Date(-1995, 2, 28));
5027             date.add!"months"(-14, AllowDayOverflow.no);
5028             assert(date == Date(-1997, 12, 28));
5029         }
5030 
5031         {
5032             auto date = Date(-2002, 12, 31);
5033             date.add!"months"(14, AllowDayOverflow.no);
5034             assert(date == Date(-2000, 2, 29));
5035             date.add!"months"(-14, AllowDayOverflow.no);
5036             assert(date == Date(-2002, 12, 29));
5037         }
5038 
5039         {
5040             auto date = Date(-2001, 12, 31);
5041             date.add!"months"(14, AllowDayOverflow.no);
5042             assert(date == Date(-1999, 2, 28));
5043             date.add!"months"(-14, AllowDayOverflow.no);
5044             assert(date == Date(-2001, 12, 28));
5045         }
5046 
5047         // Test Both
5048         {
5049             auto date = Date(1, 1, 1);
5050             date.add!"months"(-1, AllowDayOverflow.no);
5051             assert(date == Date(0, 12, 1));
5052             date.add!"months"(1, AllowDayOverflow.no);
5053             assert(date == Date(1, 1, 1));
5054         }
5055 
5056         {
5057             auto date = Date(4, 1, 1);
5058             date.add!"months"(-48, AllowDayOverflow.no);
5059             assert(date == Date(0, 1, 1));
5060             date.add!"months"(48, AllowDayOverflow.no);
5061             assert(date == Date(4, 1, 1));
5062         }
5063 
5064         {
5065             auto date = Date(4, 3, 31);
5066             date.add!"months"(-49, AllowDayOverflow.no);
5067             assert(date == Date(0, 2, 29));
5068             date.add!"months"(49, AllowDayOverflow.no);
5069             assert(date == Date(4, 3, 29));
5070         }
5071 
5072         {
5073             auto date = Date(4, 3, 31);
5074             date.add!"months"(-85, AllowDayOverflow.no);
5075             assert(date == Date(-3, 2, 28));
5076             date.add!"months"(85, AllowDayOverflow.no);
5077             assert(date == Date(4, 3, 28));
5078         }
5079 
5080         {
5081             auto date = Date(-3, 3, 31);
5082             date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
5083             assert(date == Date(-3, 5, 30));
5084         }
5085     }
5086 
5087 
5088     /++
5089         Adds the given number of years or months to this $(LREF Date). A negative
5090         number will subtract.
5091 
5092         The difference between rolling and adding is that rolling does not
5093         affect larger units. Rolling a $(LREF Date) 12 months gets
5094         the exact same $(LREF Date). However, the days can still be affected due
5095         to the differing number of days in each month.
5096 
5097         Because there are no units larger than years, there is no difference
5098         between adding and rolling years.
5099 
5100         Params:
5101             units         = The type of units to add ("years" or "months").
5102             value         = The number of months or years to add to this
5103                             $(LREF Date).
5104             allowOverflow = Whether the day should be allowed to overflow,
5105                             causing the month to increment.
5106       +/
5107     @safe pure nothrow @nogc
5108     ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5109         if (units == "years")
5110     {
5111         return add!"years"(value, allowOverflow);
5112     }
5113 
5114     ///
5115     @safe unittest
5116     {
5117         auto d1 = Date(2010, 1, 1);
5118         d1.roll!"months"(1);
5119         assert(d1 == Date(2010, 2, 1));
5120 
5121         auto d2 = Date(2010, 1, 1);
5122         d2.roll!"months"(-1);
5123         assert(d2 == Date(2010, 12, 1));
5124 
5125         auto d3 = Date(1999, 1, 29);
5126         d3.roll!"months"(1);
5127         assert(d3 == Date(1999, 3, 1));
5128 
5129         auto d4 = Date(1999, 1, 29);
5130         d4.roll!"months"(1, AllowDayOverflow.no);
5131         assert(d4 == Date(1999, 2, 28));
5132 
5133         auto d5 = Date(2000, 2, 29);
5134         d5.roll!"years"(1);
5135         assert(d5 == Date(2001, 3, 1));
5136 
5137         auto d6 = Date(2000, 2, 29);
5138         d6.roll!"years"(1, AllowDayOverflow.no);
5139         assert(d6 == Date(2001, 2, 28));
5140     }
5141 
5142     @safe unittest
5143     {
5144         const cdate = Date(1999, 7, 6);
5145         immutable idate = Date(1999, 7, 6);
5146         static assert(!__traits(compiles, cdate.roll!"years"(3)));
5147         static assert(!__traits(compiles, idate.rolYears(3)));
5148     }
5149 
5150 
5151     // Shares documentation with "years" version.
5152     @safe pure nothrow @nogc
5153     ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5154         if (units == "months")
5155     {
5156         months %= 12;
5157         auto newMonth = _month + months;
5158 
5159         if (months < 0)
5160         {
5161             if (newMonth < 1)
5162                 newMonth += 12;
5163         }
5164         else
5165         {
5166             if (newMonth > 12)
5167                 newMonth -= 12;
5168         }
5169 
5170         _month = cast(Month) newMonth;
5171 
5172         immutable currMaxDay = maxDay(_year, _month);
5173         immutable overflow = _day - currMaxDay;
5174 
5175         if (overflow > 0)
5176         {
5177             if (allowOverflow == AllowDayOverflow.yes)
5178             {
5179                 ++_month;
5180                 _day = cast(ubyte) overflow;
5181             }
5182             else
5183                 _day = cast(ubyte) currMaxDay;
5184         }
5185 
5186         return this;
5187     }
5188 
5189     // Test roll!"months"() with AllowDayOverflow.yes
5190     @safe unittest
5191     {
5192         // Test A.D.
5193         {
5194             auto date = Date(1999, 7, 6);
5195             date.roll!"months"(3);
5196             assert(date == Date(1999, 10, 6));
5197             date.roll!"months"(-4);
5198             assert(date == Date(1999, 6, 6));
5199         }
5200 
5201         {
5202             auto date = Date(1999, 7, 6);
5203             date.roll!"months"(6);
5204             assert(date == Date(1999, 1, 6));
5205             date.roll!"months"(-6);
5206             assert(date == Date(1999, 7, 6));
5207         }
5208 
5209         {
5210             auto date = Date(1999, 7, 6);
5211             date.roll!"months"(27);
5212             assert(date == Date(1999, 10, 6));
5213             date.roll!"months"(-28);
5214             assert(date == Date(1999, 6, 6));
5215         }
5216 
5217         {
5218             auto date = Date(1999, 5, 31);
5219             date.roll!"months"(1);
5220             assert(date == Date(1999, 7, 1));
5221         }
5222 
5223         {
5224             auto date = Date(1999, 5, 31);
5225             date.roll!"months"(-1);
5226             assert(date == Date(1999, 5, 1));
5227         }
5228 
5229         {
5230             auto date = Date(1999, 2, 28);
5231             date.roll!"months"(12);
5232             assert(date == Date(1999, 2, 28));
5233         }
5234 
5235         {
5236             auto date = Date(2000, 2, 29);
5237             date.roll!"months"(12);
5238             assert(date == Date(2000, 2, 29));
5239         }
5240 
5241         {
5242             auto date = Date(1999, 7, 31);
5243             date.roll!"months"(1);
5244             assert(date == Date(1999, 8, 31));
5245             date.roll!"months"(1);
5246             assert(date == Date(1999, 10, 1));
5247         }
5248 
5249         {
5250             auto date = Date(1998, 8, 31);
5251             date.roll!"months"(13);
5252             assert(date == Date(1998, 10, 1));
5253             date.roll!"months"(-13);
5254             assert(date == Date(1998, 9, 1));
5255         }
5256 
5257         {
5258             auto date = Date(1997, 12, 31);
5259             date.roll!"months"(13);
5260             assert(date == Date(1997, 1, 31));
5261             date.roll!"months"(-13);
5262             assert(date == Date(1997, 12, 31));
5263         }
5264 
5265         {
5266             auto date = Date(1997, 12, 31);
5267             date.roll!"months"(14);
5268             assert(date == Date(1997, 3, 3));
5269             date.roll!"months"(-14);
5270             assert(date == Date(1997, 1, 3));
5271         }
5272 
5273         {
5274             auto date = Date(1998, 12, 31);
5275             date.roll!"months"(14);
5276             assert(date == Date(1998, 3, 3));
5277             date.roll!"months"(-14);
5278             assert(date == Date(1998, 1, 3));
5279         }
5280 
5281         {
5282             auto date = Date(1999, 12, 31);
5283             date.roll!"months"(14);
5284             assert(date == Date(1999, 3, 3));
5285             date.roll!"months"(-14);
5286             assert(date == Date(1999, 1, 3));
5287         }
5288 
5289         // Test B.C.
5290         {
5291             auto date = Date(-1999, 7, 6);
5292             date.roll!"months"(3);
5293             assert(date == Date(-1999, 10, 6));
5294             date.roll!"months"(-4);
5295             assert(date == Date(-1999, 6, 6));
5296         }
5297 
5298         {
5299             auto date = Date(-1999, 7, 6);
5300             date.roll!"months"(6);
5301             assert(date == Date(-1999, 1, 6));
5302             date.roll!"months"(-6);
5303             assert(date == Date(-1999, 7, 6));
5304         }
5305 
5306         {
5307             auto date = Date(-1999, 7, 6);
5308             date.roll!"months"(-27);
5309             assert(date == Date(-1999, 4, 6));
5310             date.roll!"months"(28);
5311             assert(date == Date(-1999, 8, 6));
5312         }
5313 
5314         {
5315             auto date = Date(-1999, 5, 31);
5316             date.roll!"months"(1);
5317             assert(date == Date(-1999, 7, 1));
5318         }
5319 
5320         {
5321             auto date = Date(-1999, 5, 31);
5322             date.roll!"months"(-1);
5323             assert(date == Date(-1999, 5, 1));
5324         }
5325 
5326         {
5327             auto date = Date(-1999, 2, 28);
5328             date.roll!"months"(-12);
5329             assert(date == Date(-1999, 2, 28));
5330         }
5331 
5332         {
5333             auto date = Date(-2000, 2, 29);
5334             date.roll!"months"(-12);
5335             assert(date == Date(-2000, 2, 29));
5336         }
5337 
5338         {
5339             auto date = Date(-1999, 7, 31);
5340             date.roll!"months"(1);
5341             assert(date == Date(-1999, 8, 31));
5342             date.roll!"months"(1);
5343             assert(date == Date(-1999, 10, 1));
5344         }
5345 
5346         {
5347             auto date = Date(-1998, 8, 31);
5348             date.roll!"months"(13);
5349             assert(date == Date(-1998, 10, 1));
5350             date.roll!"months"(-13);
5351             assert(date == Date(-1998, 9, 1));
5352         }
5353 
5354         {
5355             auto date = Date(-1997, 12, 31);
5356             date.roll!"months"(13);
5357             assert(date == Date(-1997, 1, 31));
5358             date.roll!"months"(-13);
5359             assert(date == Date(-1997, 12, 31));
5360         }
5361 
5362         {
5363             auto date = Date(-1997, 12, 31);
5364             date.roll!"months"(14);
5365             assert(date == Date(-1997, 3, 3));
5366             date.roll!"months"(-14);
5367             assert(date == Date(-1997, 1, 3));
5368         }
5369 
5370         {
5371             auto date = Date(-2002, 12, 31);
5372             date.roll!"months"(14);
5373             assert(date == Date(-2002, 3, 3));
5374             date.roll!"months"(-14);
5375             assert(date == Date(-2002, 1, 3));
5376         }
5377 
5378         {
5379             auto date = Date(-2001, 12, 31);
5380             date.roll!"months"(14);
5381             assert(date == Date(-2001, 3, 3));
5382             date.roll!"months"(-14);
5383             assert(date == Date(-2001, 1, 3));
5384         }
5385 
5386         // Test Both
5387         {
5388             auto date = Date(1, 1, 1);
5389             date.roll!"months"(-1);
5390             assert(date == Date(1, 12, 1));
5391             date.roll!"months"(1);
5392             assert(date == Date(1, 1, 1));
5393         }
5394 
5395         {
5396             auto date = Date(4, 1, 1);
5397             date.roll!"months"(-48);
5398             assert(date == Date(4, 1, 1));
5399             date.roll!"months"(48);
5400             assert(date == Date(4, 1, 1));
5401         }
5402 
5403         {
5404             auto date = Date(4, 3, 31);
5405             date.roll!"months"(-49);
5406             assert(date == Date(4, 3, 2));
5407             date.roll!"months"(49);
5408             assert(date == Date(4, 4, 2));
5409         }
5410 
5411         {
5412             auto date = Date(4, 3, 31);
5413             date.roll!"months"(-85);
5414             assert(date == Date(4, 3, 2));
5415             date.roll!"months"(85);
5416             assert(date == Date(4, 4, 2));
5417         }
5418 
5419         {
5420             auto date = Date(-1, 1, 1);
5421             date.roll!"months"(-1);
5422             assert(date == Date(-1, 12, 1));
5423             date.roll!"months"(1);
5424             assert(date == Date(-1, 1, 1));
5425         }
5426 
5427         {
5428             auto date = Date(-4, 1, 1);
5429             date.roll!"months"(-48);
5430             assert(date == Date(-4, 1, 1));
5431             date.roll!"months"(48);
5432             assert(date == Date(-4, 1, 1));
5433         }
5434 
5435         {
5436             auto date = Date(-4, 3, 31);
5437             date.roll!"months"(-49);
5438             assert(date == Date(-4, 3, 2));
5439             date.roll!"months"(49);
5440             assert(date == Date(-4, 4, 2));
5441         }
5442 
5443         {
5444             auto date = Date(-4, 3, 31);
5445             date.roll!"months"(-85);
5446             assert(date == Date(-4, 3, 2));
5447             date.roll!"months"(85);
5448             assert(date == Date(-4, 4, 2));
5449         }
5450 
5451         {
5452             auto date = Date(-3, 3, 31);
5453             date.roll!"months"(85).roll!"months"(-83);
5454             assert(date == Date(-3, 6, 1));
5455         }
5456 
5457         const cdate = Date(1999, 7, 6);
5458         immutable idate = Date(1999, 7, 6);
5459         static assert(!__traits(compiles, cdate.roll!"months"(3)));
5460         static assert(!__traits(compiles, idate.roll!"months"(3)));
5461     }
5462 
5463     // Test roll!"months"() with AllowDayOverflow.no
5464     @safe unittest
5465     {
5466         // Test A.D.
5467         {
5468             auto date = Date(1999, 7, 6);
5469             date.roll!"months"(3, AllowDayOverflow.no);
5470             assert(date == Date(1999, 10, 6));
5471             date.roll!"months"(-4, AllowDayOverflow.no);
5472             assert(date == Date(1999, 6, 6));
5473         }
5474 
5475         {
5476             auto date = Date(1999, 7, 6);
5477             date.roll!"months"(6, AllowDayOverflow.no);
5478             assert(date == Date(1999, 1, 6));
5479             date.roll!"months"(-6, AllowDayOverflow.no);
5480             assert(date == Date(1999, 7, 6));
5481         }
5482 
5483         {
5484             auto date = Date(1999, 7, 6);
5485             date.roll!"months"(27, AllowDayOverflow.no);
5486             assert(date == Date(1999, 10, 6));
5487             date.roll!"months"(-28, AllowDayOverflow.no);
5488             assert(date == Date(1999, 6, 6));
5489         }
5490 
5491         {
5492             auto date = Date(1999, 5, 31);
5493             date.roll!"months"(1, AllowDayOverflow.no);
5494             assert(date == Date(1999, 6, 30));
5495         }
5496 
5497         {
5498             auto date = Date(1999, 5, 31);
5499             date.roll!"months"(-1, AllowDayOverflow.no);
5500             assert(date == Date(1999, 4, 30));
5501         }
5502 
5503         {
5504             auto date = Date(1999, 2, 28);
5505             date.roll!"months"(12, AllowDayOverflow.no);
5506             assert(date == Date(1999, 2, 28));
5507         }
5508 
5509         {
5510             auto date = Date(2000, 2, 29);
5511             date.roll!"months"(12, AllowDayOverflow.no);
5512             assert(date == Date(2000, 2, 29));
5513         }
5514 
5515         {
5516             auto date = Date(1999, 7, 31);
5517             date.roll!"months"(1, AllowDayOverflow.no);
5518             assert(date == Date(1999, 8, 31));
5519             date.roll!"months"(1, AllowDayOverflow.no);
5520             assert(date == Date(1999, 9, 30));
5521         }
5522 
5523         {
5524             auto date = Date(1998, 8, 31);
5525             date.roll!"months"(13, AllowDayOverflow.no);
5526             assert(date == Date(1998, 9, 30));
5527             date.roll!"months"(-13, AllowDayOverflow.no);
5528             assert(date == Date(1998, 8, 30));
5529         }
5530 
5531         {
5532             auto date = Date(1997, 12, 31);
5533             date.roll!"months"(13, AllowDayOverflow.no);
5534             assert(date == Date(1997, 1, 31));
5535             date.roll!"months"(-13, AllowDayOverflow.no);
5536             assert(date == Date(1997, 12, 31));
5537         }
5538 
5539         {
5540             auto date = Date(1997, 12, 31);
5541             date.roll!"months"(14, AllowDayOverflow.no);
5542             assert(date == Date(1997, 2, 28));
5543             date.roll!"months"(-14, AllowDayOverflow.no);
5544             assert(date == Date(1997, 12, 28));
5545         }
5546 
5547         {
5548             auto date = Date(1998, 12, 31);
5549             date.roll!"months"(14, AllowDayOverflow.no);
5550             assert(date == Date(1998, 2, 28));
5551             date.roll!"months"(-14, AllowDayOverflow.no);
5552             assert(date == Date(1998, 12, 28));
5553         }
5554 
5555         {
5556             auto date = Date(1999, 12, 31);
5557             date.roll!"months"(14, AllowDayOverflow.no);
5558             assert(date == Date(1999, 2, 28));
5559             date.roll!"months"(-14, AllowDayOverflow.no);
5560             assert(date == Date(1999, 12, 28));
5561         }
5562 
5563         // Test B.C.
5564         {
5565             auto date = Date(-1999, 7, 6);
5566             date.roll!"months"(3, AllowDayOverflow.no);
5567             assert(date == Date(-1999, 10, 6));
5568             date.roll!"months"(-4, AllowDayOverflow.no);
5569             assert(date == Date(-1999, 6, 6));
5570         }
5571 
5572         {
5573             auto date = Date(-1999, 7, 6);
5574             date.roll!"months"(6, AllowDayOverflow.no);
5575             assert(date == Date(-1999, 1, 6));
5576             date.roll!"months"(-6, AllowDayOverflow.no);
5577             assert(date == Date(-1999, 7, 6));
5578         }
5579 
5580         {
5581             auto date = Date(-1999, 7, 6);
5582             date.roll!"months"(-27, AllowDayOverflow.no);
5583             assert(date == Date(-1999, 4, 6));
5584             date.roll!"months"(28, AllowDayOverflow.no);
5585             assert(date == Date(-1999, 8, 6));
5586         }
5587 
5588         {
5589             auto date = Date(-1999, 5, 31);
5590             date.roll!"months"(1, AllowDayOverflow.no);
5591             assert(date == Date(-1999, 6, 30));
5592         }
5593 
5594         {
5595             auto date = Date(-1999, 5, 31);
5596             date.roll!"months"(-1, AllowDayOverflow.no);
5597             assert(date == Date(-1999, 4, 30));
5598         }
5599 
5600         {
5601             auto date = Date(-1999, 2, 28);
5602             date.roll!"months"(-12, AllowDayOverflow.no);
5603             assert(date == Date(-1999, 2, 28));
5604         }
5605 
5606         {
5607             auto date = Date(-2000, 2, 29);
5608             date.roll!"months"(-12, AllowDayOverflow.no);
5609             assert(date == Date(-2000, 2, 29));
5610         }
5611 
5612         {
5613             auto date = Date(-1999, 7, 31);
5614             date.roll!"months"(1, AllowDayOverflow.no);
5615             assert(date == Date(-1999, 8, 31));
5616             date.roll!"months"(1, AllowDayOverflow.no);
5617             assert(date == Date(-1999, 9, 30));
5618         }
5619 
5620         {
5621             auto date = Date(-1998, 8, 31);
5622             date.roll!"months"(13, AllowDayOverflow.no);
5623             assert(date == Date(-1998, 9, 30));
5624             date.roll!"months"(-13, AllowDayOverflow.no);
5625             assert(date == Date(-1998, 8, 30));
5626         }
5627 
5628         {
5629             auto date = Date(-1997, 12, 31);
5630             date.roll!"months"(13, AllowDayOverflow.no);
5631             assert(date == Date(-1997, 1, 31));
5632             date.roll!"months"(-13, AllowDayOverflow.no);
5633             assert(date == Date(-1997, 12, 31));
5634         }
5635 
5636         {
5637             auto date = Date(-1997, 12, 31);
5638             date.roll!"months"(14, AllowDayOverflow.no);
5639             assert(date == Date(-1997, 2, 28));
5640             date.roll!"months"(-14, AllowDayOverflow.no);
5641             assert(date == Date(-1997, 12, 28));
5642         }
5643 
5644         {
5645             auto date = Date(-2002, 12, 31);
5646             date.roll!"months"(14, AllowDayOverflow.no);
5647             assert(date == Date(-2002, 2, 28));
5648             date.roll!"months"(-14, AllowDayOverflow.no);
5649             assert(date == Date(-2002, 12, 28));
5650         }
5651 
5652         {
5653             auto date = Date(-2001, 12, 31);
5654             date.roll!"months"(14, AllowDayOverflow.no);
5655             assert(date == Date(-2001, 2, 28));
5656             date.roll!"months"(-14, AllowDayOverflow.no);
5657             assert(date == Date(-2001, 12, 28));
5658         }
5659 
5660         // Test Both
5661         {
5662             auto date = Date(1, 1, 1);
5663             date.roll!"months"(-1, AllowDayOverflow.no);
5664             assert(date == Date(1, 12, 1));
5665             date.roll!"months"(1, AllowDayOverflow.no);
5666             assert(date == Date(1, 1, 1));
5667         }
5668 
5669         {
5670             auto date = Date(4, 1, 1);
5671             date.roll!"months"(-48, AllowDayOverflow.no);
5672             assert(date == Date(4, 1, 1));
5673             date.roll!"months"(48, AllowDayOverflow.no);
5674             assert(date == Date(4, 1, 1));
5675         }
5676 
5677         {
5678             auto date = Date(4, 3, 31);
5679             date.roll!"months"(-49, AllowDayOverflow.no);
5680             assert(date == Date(4, 2, 29));
5681             date.roll!"months"(49, AllowDayOverflow.no);
5682             assert(date == Date(4, 3, 29));
5683         }
5684 
5685         {
5686             auto date = Date(4, 3, 31);
5687             date.roll!"months"(-85, AllowDayOverflow.no);
5688             assert(date == Date(4, 2, 29));
5689             date.roll!"months"(85, AllowDayOverflow.no);
5690             assert(date == Date(4, 3, 29));
5691         }
5692 
5693         {
5694             auto date = Date(-1, 1, 1);
5695             date.roll!"months"(-1, AllowDayOverflow.no);
5696             assert(date == Date(-1, 12, 1));
5697             date.roll!"months"(1, AllowDayOverflow.no);
5698             assert(date == Date(-1, 1, 1));
5699         }
5700 
5701         {
5702             auto date = Date(-4, 1, 1);
5703             date.roll!"months"(-48, AllowDayOverflow.no);
5704             assert(date == Date(-4, 1, 1));
5705             date.roll!"months"(48, AllowDayOverflow.no);
5706             assert(date == Date(-4, 1, 1));
5707         }
5708 
5709         {
5710             auto date = Date(-4, 3, 31);
5711             date.roll!"months"(-49, AllowDayOverflow.no);
5712             assert(date == Date(-4, 2, 29));
5713             date.roll!"months"(49, AllowDayOverflow.no);
5714             assert(date == Date(-4, 3, 29));
5715         }
5716 
5717         {
5718             auto date = Date(-4, 3, 31);
5719             date.roll!"months"(-85, AllowDayOverflow.no);
5720             assert(date == Date(-4, 2, 29));
5721             date.roll!"months"(85, AllowDayOverflow.no);
5722             assert(date == Date(-4, 3, 29));
5723         }
5724 
5725         {
5726             auto date = Date(-3, 3, 31);
5727             date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
5728             assert(date == Date(-3, 5, 30));
5729         }
5730     }
5731 
5732 
5733     /++
5734         Adds the given number of units to this $(LREF Date). A negative number
5735         will subtract.
5736 
5737         The difference between rolling and adding is that rolling does not
5738         affect larger units. For instance, rolling a $(LREF Date) one
5739         year's worth of days gets the exact same $(LREF Date).
5740 
5741         The only accepted units are $(D "days").
5742 
5743         Params:
5744             units = The units to add. Must be $(D "days").
5745             days  = The number of days to add to this $(LREF Date).
5746       +/
5747     ref Date roll(string units)(long days) @safe pure nothrow @nogc
5748         if (units == "days")
5749     {
5750         immutable limit = maxDay(_year, _month);
5751         days %= limit;
5752         auto newDay = _day + days;
5753 
5754         if (days < 0)
5755         {
5756             if (newDay < 1)
5757                 newDay += limit;
5758         }
5759         else if (newDay > limit)
5760             newDay -= limit;
5761 
5762         _day = cast(ubyte) newDay;
5763         return this;
5764     }
5765 
5766     ///
5767     @safe unittest
5768     {
5769         auto d = Date(2010, 1, 1);
5770         d.roll!"days"(1);
5771         assert(d == Date(2010, 1, 2));
5772         d.roll!"days"(365);
5773         assert(d == Date(2010, 1, 26));
5774         d.roll!"days"(-32);
5775         assert(d == Date(2010, 1, 25));
5776     }
5777 
5778     @safe unittest
5779     {
5780         // Test A.D.
5781         {
5782             auto date = Date(1999, 2, 28);
5783             date.roll!"days"(1);
5784             assert(date == Date(1999, 2, 1));
5785             date.roll!"days"(-1);
5786             assert(date == Date(1999, 2, 28));
5787         }
5788 
5789         {
5790             auto date = Date(2000, 2, 28);
5791             date.roll!"days"(1);
5792             assert(date == Date(2000, 2, 29));
5793             date.roll!"days"(1);
5794             assert(date == Date(2000, 2, 1));
5795             date.roll!"days"(-1);
5796             assert(date == Date(2000, 2, 29));
5797         }
5798 
5799         {
5800             auto date = Date(1999, 6, 30);
5801             date.roll!"days"(1);
5802             assert(date == Date(1999, 6, 1));
5803             date.roll!"days"(-1);
5804             assert(date == Date(1999, 6, 30));
5805         }
5806 
5807         {
5808             auto date = Date(1999, 7, 31);
5809             date.roll!"days"(1);
5810             assert(date == Date(1999, 7, 1));
5811             date.roll!"days"(-1);
5812             assert(date == Date(1999, 7, 31));
5813         }
5814 
5815         {
5816             auto date = Date(1999, 1, 1);
5817             date.roll!"days"(-1);
5818             assert(date == Date(1999, 1, 31));
5819             date.roll!"days"(1);
5820             assert(date == Date(1999, 1, 1));
5821         }
5822 
5823         {
5824             auto date = Date(1999, 7, 6);
5825             date.roll!"days"(9);
5826             assert(date == Date(1999, 7, 15));
5827             date.roll!"days"(-11);
5828             assert(date == Date(1999, 7, 4));
5829             date.roll!"days"(30);
5830             assert(date == Date(1999, 7, 3));
5831             date.roll!"days"(-3);
5832             assert(date == Date(1999, 7, 31));
5833         }
5834 
5835         {
5836             auto date = Date(1999, 7, 6);
5837             date.roll!"days"(365);
5838             assert(date == Date(1999, 7, 30));
5839             date.roll!"days"(-365);
5840             assert(date == Date(1999, 7, 6));
5841             date.roll!"days"(366);
5842             assert(date == Date(1999, 7, 31));
5843             date.roll!"days"(730);
5844             assert(date == Date(1999, 7, 17));
5845             date.roll!"days"(-1096);
5846             assert(date == Date(1999, 7, 6));
5847         }
5848 
5849         {
5850             auto date = Date(1999, 2, 6);
5851             date.roll!"days"(365);
5852             assert(date == Date(1999, 2, 7));
5853             date.roll!"days"(-365);
5854             assert(date == Date(1999, 2, 6));
5855             date.roll!"days"(366);
5856             assert(date == Date(1999, 2, 8));
5857             date.roll!"days"(730);
5858             assert(date == Date(1999, 2, 10));
5859             date.roll!"days"(-1096);
5860             assert(date == Date(1999, 2, 6));
5861         }
5862 
5863         // Test B.C.
5864         {
5865             auto date = Date(-1999, 2, 28);
5866             date.roll!"days"(1);
5867             assert(date == Date(-1999, 2, 1));
5868             date.roll!"days"(-1);
5869             assert(date == Date(-1999, 2, 28));
5870         }
5871 
5872         {
5873             auto date = Date(-2000, 2, 28);
5874             date.roll!"days"(1);
5875             assert(date == Date(-2000, 2, 29));
5876             date.roll!"days"(1);
5877             assert(date == Date(-2000, 2, 1));
5878             date.roll!"days"(-1);
5879             assert(date == Date(-2000, 2, 29));
5880         }
5881 
5882         {
5883             auto date = Date(-1999, 6, 30);
5884             date.roll!"days"(1);
5885             assert(date == Date(-1999, 6, 1));
5886             date.roll!"days"(-1);
5887             assert(date == Date(-1999, 6, 30));
5888         }
5889 
5890         {
5891             auto date = Date(-1999, 7, 31);
5892             date.roll!"days"(1);
5893             assert(date == Date(-1999, 7, 1));
5894             date.roll!"days"(-1);
5895             assert(date == Date(-1999, 7, 31));
5896         }
5897 
5898         {
5899             auto date = Date(-1999, 1, 1);
5900             date.roll!"days"(-1);
5901             assert(date == Date(-1999, 1, 31));
5902             date.roll!"days"(1);
5903             assert(date == Date(-1999, 1, 1));
5904         }
5905 
5906         {
5907             auto date = Date(-1999, 7, 6);
5908             date.roll!"days"(9);
5909             assert(date == Date(-1999, 7, 15));
5910             date.roll!"days"(-11);
5911             assert(date == Date(-1999, 7, 4));
5912             date.roll!"days"(30);
5913             assert(date == Date(-1999, 7, 3));
5914             date.roll!"days"(-3);
5915             assert(date == Date(-1999, 7, 31));
5916         }
5917 
5918         {
5919             auto date = Date(-1999, 7, 6);
5920             date.roll!"days"(365);
5921             assert(date == Date(-1999, 7, 30));
5922             date.roll!"days"(-365);
5923             assert(date == Date(-1999, 7, 6));
5924             date.roll!"days"(366);
5925             assert(date == Date(-1999, 7, 31));
5926             date.roll!"days"(730);
5927             assert(date == Date(-1999, 7, 17));
5928             date.roll!"days"(-1096);
5929             assert(date == Date(-1999, 7, 6));
5930         }
5931 
5932         // Test Both
5933         {
5934             auto date = Date(1, 7, 6);
5935             date.roll!"days"(-365);
5936             assert(date == Date(1, 7, 13));
5937             date.roll!"days"(365);
5938             assert(date == Date(1, 7, 6));
5939             date.roll!"days"(-731);
5940             assert(date == Date(1, 7, 19));
5941             date.roll!"days"(730);
5942             assert(date == Date(1, 7, 5));
5943         }
5944 
5945         {
5946             auto date = Date(0, 7, 6);
5947             date.roll!"days"(-365);
5948             assert(date == Date(0, 7, 13));
5949             date.roll!"days"(365);
5950             assert(date == Date(0, 7, 6));
5951             date.roll!"days"(-731);
5952             assert(date == Date(0, 7, 19));
5953             date.roll!"days"(730);
5954             assert(date == Date(0, 7, 5));
5955         }
5956 
5957         {
5958             auto date = Date(0, 7, 6);
5959             date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
5960             assert(date == Date(0, 7, 8));
5961         }
5962 
5963         const cdate = Date(1999, 7, 6);
5964         immutable idate = Date(1999, 7, 6);
5965         static assert(!__traits(compiles, cdate.roll!"days"(12)));
5966         static assert(!__traits(compiles, idate.roll!"days"(12)));
5967     }
5968 
5969 
5970     /++
5971         Gives the result of adding or subtracting a $(REF Duration, core,time)
5972         from
5973 
5974         The legal types of arithmetic for $(LREF Date) using this operator are
5975 
5976         $(BOOKTABLE,
5977         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
5978         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
5979         )
5980 
5981         Params:
5982             duration = The $(REF Duration, core,time) to add to or subtract from
5983                        this $(LREF Date).
5984       +/
5985     Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
5986         if (op == "+" || op == "-")
5987     {
5988         Date retval = this;
5989         immutable days = duration.total!"days";
5990         mixin("return retval._addDays(" ~ op ~ "days);");
5991     }
5992 
5993     ///
5994     @safe unittest
5995     {
5996         import core.time : days;
5997 
5998         assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
5999         assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
6000 
6001         assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
6002         assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
6003     }
6004 
6005     @safe unittest
6006     {
6007         auto date = Date(1999, 7, 6);
6008 
6009         assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
6010         assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
6011         assert(date + dur!"days"(7) == Date(1999, 7, 13));
6012         assert(date + dur!"days"(-7) == Date(1999, 6, 29));
6013 
6014         assert(date + dur!"hours"(24) == Date(1999, 7, 7));
6015         assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
6016         assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
6017         assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
6018         assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
6019         assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6020         assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6021         assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6022         assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6023         assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6024         assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6025         assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6026 
6027         assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
6028         assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
6029         assert(date - dur!"days"(-7) == Date(1999, 7, 13));
6030         assert(date - dur!"days"(7) == Date(1999, 6, 29));
6031 
6032         assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
6033         assert(date - dur!"hours"(24) == Date(1999, 7, 5));
6034         assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
6035         assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
6036         assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6037         assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
6038         assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6039         assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6040         assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6041         assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6042         assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6043         assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6044 
6045         auto duration = dur!"days"(12);
6046         const cdate = Date(1999, 7, 6);
6047         immutable idate = Date(1999, 7, 6);
6048         assert(date + duration == Date(1999, 7, 18));
6049         assert(cdate + duration == Date(1999, 7, 18));
6050         assert(idate + duration == Date(1999, 7, 18));
6051 
6052         assert(date - duration == Date(1999, 6, 24));
6053         assert(cdate - duration == Date(1999, 6, 24));
6054         assert(idate - duration == Date(1999, 6, 24));
6055     }
6056 
6057 
6058     /++
6059         Gives the result of adding or subtracting a $(REF Duration, core,time)
6060         from this $(LREF Date), as well as assigning the result to this
6061         $(LREF Date).
6062 
6063         The legal types of arithmetic for $(LREF Date) using this operator are
6064 
6065         $(BOOKTABLE,
6066         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6067         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6068         )
6069 
6070         Params:
6071             duration = The $(REF Duration, core,time) to add to or subtract from
6072                        this $(LREF Date).
6073       +/
6074     ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
6075         if (op == "+" || op == "-")
6076     {
6077         immutable days = duration.total!"days";
6078         mixin("return _addDays(" ~ op ~ "days);");
6079     }
6080 
6081     @safe unittest
6082     {
6083         assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
6084         assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
6085         assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
6086         assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
6087 
6088         assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
6089         assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
6090         assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
6091         assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
6092         assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
6093         assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6094         assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6095         assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6096         assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6097         assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6098         assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6099         assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6100 
6101         assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
6102         assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
6103         assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
6104         assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
6105 
6106         assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
6107         assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
6108         assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
6109         assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
6110         assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6111         assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
6112         assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6113         assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6114         assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6115         assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6116         assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6117         assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6118 
6119         {
6120             auto date = Date(0, 1, 31);
6121             (date += dur!"days"(507)) += dur!"days"(-2);
6122             assert(date == Date(1, 6, 19));
6123         }
6124 
6125         auto duration = dur!"days"(12);
6126         auto date = Date(1999, 7, 6);
6127         const cdate = Date(1999, 7, 6);
6128         immutable idate = Date(1999, 7, 6);
6129         date += duration;
6130         static assert(!__traits(compiles, cdate += duration));
6131         static assert(!__traits(compiles, idate += duration));
6132 
6133         date -= duration;
6134         static assert(!__traits(compiles, cdate -= duration));
6135         static assert(!__traits(compiles, idate -= duration));
6136     }
6137 
6138 
6139     /++
6140         Gives the difference between two $(LREF Date)s.
6141 
6142         The legal types of arithmetic for $(LREF Date) using this operator are
6143 
6144         $(BOOKTABLE,
6145         $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
6146         )
6147       +/
6148     Duration opBinary(string op)(in Date rhs) const @safe pure nothrow @nogc
6149         if (op == "-")
6150     {
6151         return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
6152     }
6153 
6154     @safe unittest
6155     {
6156         auto date = Date(1999, 7, 6);
6157 
6158         assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
6159         assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
6160         assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
6161         assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
6162         assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
6163         assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
6164 
6165         const cdate = Date(1999, 7, 6);
6166         immutable idate = Date(1999, 7, 6);
6167         assert(date - date == Duration.zero);
6168         assert(cdate - date == Duration.zero);
6169         assert(idate - date == Duration.zero);
6170 
6171         assert(date - cdate == Duration.zero);
6172         assert(cdate - cdate == Duration.zero);
6173         assert(idate - cdate == Duration.zero);
6174 
6175         assert(date - idate == Duration.zero);
6176         assert(cdate - idate == Duration.zero);
6177         assert(idate - idate == Duration.zero);
6178     }
6179 
6180 
6181     /++
6182         Returns the difference between the two $(LREF Date)s in months.
6183 
6184         To get the difference in years, subtract the year property
6185         of two $(LREF Date)s. To get the difference in days or weeks,
6186         subtract the $(LREF Date)s themselves and use the
6187         $(REF Duration, core,time) that results. Because converting between
6188         months and smaller units requires a specific date (which
6189         $(REF Duration, core,time)s don't have), getting the difference in
6190         months requires some math using both the year and month properties, so
6191         this is a convenience function for getting the difference in months.
6192 
6193         Note that the number of days in the months or how far into the month
6194         either $(LREF Date) is is irrelevant. It is the difference in the month
6195         property combined with the difference in years * 12. So, for instance,
6196         December 31st and January 1st are one month apart just as December 1st
6197         and January 31st are one month apart.
6198 
6199         Params:
6200             rhs = The $(LREF Date) to subtract from this one.
6201       +/
diffMonths(in Date rhs)6202     int diffMonths(in Date rhs) const @safe pure nothrow @nogc
6203     {
6204         immutable yearDiff = _year - rhs._year;
6205         immutable monthDiff = _month - rhs._month;
6206 
6207         return yearDiff * 12 + monthDiff;
6208     }
6209 
6210     ///
6211     @safe unittest
6212     {
6213         assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
6214         assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
6215         assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
6216         assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
6217     }
6218 
6219     @safe unittest
6220     {
6221         auto date = Date(1999, 7, 6);
6222 
6223         // Test A.D.
6224         assert(date.diffMonths(Date(1998, 6, 5)) == 13);
6225         assert(date.diffMonths(Date(1998, 7, 5)) == 12);
6226         assert(date.diffMonths(Date(1998, 8, 5)) == 11);
6227         assert(date.diffMonths(Date(1998, 9, 5)) == 10);
6228         assert(date.diffMonths(Date(1998, 10, 5)) == 9);
6229         assert(date.diffMonths(Date(1998, 11, 5)) == 8);
6230         assert(date.diffMonths(Date(1998, 12, 5)) == 7);
6231         assert(date.diffMonths(Date(1999, 1, 5)) == 6);
6232         assert(date.diffMonths(Date(1999, 2, 6)) == 5);
6233         assert(date.diffMonths(Date(1999, 3, 6)) == 4);
6234         assert(date.diffMonths(Date(1999, 4, 6)) == 3);
6235         assert(date.diffMonths(Date(1999, 5, 6)) == 2);
6236         assert(date.diffMonths(Date(1999, 6, 6)) == 1);
6237         assert(date.diffMonths(date) == 0);
6238         assert(date.diffMonths(Date(1999, 8, 6)) == -1);
6239         assert(date.diffMonths(Date(1999, 9, 6)) == -2);
6240         assert(date.diffMonths(Date(1999, 10, 6)) == -3);
6241         assert(date.diffMonths(Date(1999, 11, 6)) == -4);
6242         assert(date.diffMonths(Date(1999, 12, 6)) == -5);
6243         assert(date.diffMonths(Date(2000, 1, 6)) == -6);
6244         assert(date.diffMonths(Date(2000, 2, 6)) == -7);
6245         assert(date.diffMonths(Date(2000, 3, 6)) == -8);
6246         assert(date.diffMonths(Date(2000, 4, 6)) == -9);
6247         assert(date.diffMonths(Date(2000, 5, 6)) == -10);
6248         assert(date.diffMonths(Date(2000, 6, 6)) == -11);
6249         assert(date.diffMonths(Date(2000, 7, 6)) == -12);
6250         assert(date.diffMonths(Date(2000, 8, 6)) == -13);
6251 
6252         assert(Date(1998, 6, 5).diffMonths(date) == -13);
6253         assert(Date(1998, 7, 5).diffMonths(date) == -12);
6254         assert(Date(1998, 8, 5).diffMonths(date) == -11);
6255         assert(Date(1998, 9, 5).diffMonths(date) == -10);
6256         assert(Date(1998, 10, 5).diffMonths(date) == -9);
6257         assert(Date(1998, 11, 5).diffMonths(date) == -8);
6258         assert(Date(1998, 12, 5).diffMonths(date) == -7);
6259         assert(Date(1999, 1, 5).diffMonths(date) == -6);
6260         assert(Date(1999, 2, 6).diffMonths(date) == -5);
6261         assert(Date(1999, 3, 6).diffMonths(date) == -4);
6262         assert(Date(1999, 4, 6).diffMonths(date) == -3);
6263         assert(Date(1999, 5, 6).diffMonths(date) == -2);
6264         assert(Date(1999, 6, 6).diffMonths(date) == -1);
6265         assert(Date(1999, 8, 6).diffMonths(date) == 1);
6266         assert(Date(1999, 9, 6).diffMonths(date) == 2);
6267         assert(Date(1999, 10, 6).diffMonths(date) == 3);
6268         assert(Date(1999, 11, 6).diffMonths(date) == 4);
6269         assert(Date(1999, 12, 6).diffMonths(date) == 5);
6270         assert(Date(2000, 1, 6).diffMonths(date) == 6);
6271         assert(Date(2000, 2, 6).diffMonths(date) == 7);
6272         assert(Date(2000, 3, 6).diffMonths(date) == 8);
6273         assert(Date(2000, 4, 6).diffMonths(date) == 9);
6274         assert(Date(2000, 5, 6).diffMonths(date) == 10);
6275         assert(Date(2000, 6, 6).diffMonths(date) == 11);
6276         assert(Date(2000, 7, 6).diffMonths(date) == 12);
6277         assert(Date(2000, 8, 6).diffMonths(date) == 13);
6278 
6279         assert(date.diffMonths(Date(1999, 6, 30)) == 1);
6280         assert(date.diffMonths(Date(1999, 7, 1)) == 0);
6281         assert(date.diffMonths(Date(1999, 7, 6)) == 0);
6282         assert(date.diffMonths(Date(1999, 7, 11)) == 0);
6283         assert(date.diffMonths(Date(1999, 7, 16)) == 0);
6284         assert(date.diffMonths(Date(1999, 7, 21)) == 0);
6285         assert(date.diffMonths(Date(1999, 7, 26)) == 0);
6286         assert(date.diffMonths(Date(1999, 7, 31)) == 0);
6287         assert(date.diffMonths(Date(1999, 8, 1)) == -1);
6288 
6289         assert(date.diffMonths(Date(1990, 6, 30)) == 109);
6290         assert(date.diffMonths(Date(1990, 7, 1)) == 108);
6291         assert(date.diffMonths(Date(1990, 7, 6)) == 108);
6292         assert(date.diffMonths(Date(1990, 7, 11)) == 108);
6293         assert(date.diffMonths(Date(1990, 7, 16)) == 108);
6294         assert(date.diffMonths(Date(1990, 7, 21)) == 108);
6295         assert(date.diffMonths(Date(1990, 7, 26)) == 108);
6296         assert(date.diffMonths(Date(1990, 7, 31)) == 108);
6297         assert(date.diffMonths(Date(1990, 8, 1)) == 107);
6298 
6299         assert(Date(1999, 6, 30).diffMonths(date) == -1);
6300         assert(Date(1999, 7, 1).diffMonths(date) == 0);
6301         assert(Date(1999, 7, 6).diffMonths(date) == 0);
6302         assert(Date(1999, 7, 11).diffMonths(date) == 0);
6303         assert(Date(1999, 7, 16).diffMonths(date) == 0);
6304         assert(Date(1999, 7, 21).diffMonths(date) == 0);
6305         assert(Date(1999, 7, 26).diffMonths(date) == 0);
6306         assert(Date(1999, 7, 31).diffMonths(date) == 0);
6307         assert(Date(1999, 8, 1).diffMonths(date) == 1);
6308 
6309         assert(Date(1990, 6, 30).diffMonths(date) == -109);
6310         assert(Date(1990, 7, 1).diffMonths(date) == -108);
6311         assert(Date(1990, 7, 6).diffMonths(date) == -108);
6312         assert(Date(1990, 7, 11).diffMonths(date) == -108);
6313         assert(Date(1990, 7, 16).diffMonths(date) == -108);
6314         assert(Date(1990, 7, 21).diffMonths(date) == -108);
6315         assert(Date(1990, 7, 26).diffMonths(date) == -108);
6316         assert(Date(1990, 7, 31).diffMonths(date) == -108);
6317         assert(Date(1990, 8, 1).diffMonths(date) == -107);
6318 
6319         // Test B.C.
6320         auto dateBC = Date(-1999, 7, 6);
6321 
6322         assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
6323         assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
6324         assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
6325         assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
6326         assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
6327         assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
6328         assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
6329         assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
6330         assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
6331         assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
6332         assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
6333         assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
6334         assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
6335         assert(dateBC.diffMonths(dateBC) == 0);
6336         assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
6337         assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
6338         assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
6339         assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
6340         assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
6341         assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
6342         assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
6343         assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
6344         assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
6345         assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
6346         assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
6347         assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
6348         assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
6349 
6350         assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
6351         assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
6352         assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
6353         assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
6354         assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
6355         assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
6356         assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
6357         assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
6358         assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
6359         assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
6360         assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
6361         assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
6362         assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
6363         assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
6364         assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
6365         assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
6366         assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
6367         assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
6368         assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
6369         assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
6370         assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
6371         assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
6372         assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
6373         assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
6374         assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
6375         assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
6376 
6377         assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
6378         assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
6379         assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
6380         assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
6381         assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
6382         assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
6383         assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
6384         assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
6385         assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
6386 
6387         assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
6388         assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
6389         assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
6390         assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
6391         assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
6392         assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
6393         assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
6394         assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
6395         assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
6396 
6397         assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
6398         assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
6399         assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
6400         assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
6401         assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
6402         assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
6403         assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
6404         assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
6405         assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
6406 
6407         assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
6408         assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
6409         assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
6410         assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
6411         assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
6412         assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
6413         assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
6414         assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
6415         assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
6416 
6417         // Test Both
6418         assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
6419         assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
6420 
6421         const cdate = Date(1999, 7, 6);
6422         immutable idate = Date(1999, 7, 6);
6423         assert(date.diffMonths(date) == 0);
6424         assert(cdate.diffMonths(date) == 0);
6425         assert(idate.diffMonths(date) == 0);
6426 
6427         assert(date.diffMonths(cdate) == 0);
6428         assert(cdate.diffMonths(cdate) == 0);
6429         assert(idate.diffMonths(cdate) == 0);
6430 
6431         assert(date.diffMonths(idate) == 0);
6432         assert(cdate.diffMonths(idate) == 0);
6433         assert(idate.diffMonths(idate) == 0);
6434     }
6435 
6436 
6437     /++
6438         Whether this $(LREF Date) is in a leap year.
6439      +/
isLeapYear()6440     @property bool isLeapYear() const @safe pure nothrow @nogc
6441     {
6442         return yearIsLeapYear(_year);
6443     }
6444 
6445     @safe unittest
6446     {
6447         auto date = Date(1999, 7, 6);
6448         const cdate = Date(1999, 7, 6);
6449         immutable idate = Date(1999, 7, 6);
6450         static assert(!__traits(compiles, date.isLeapYear = true));
6451         static assert(!__traits(compiles, cdate.isLeapYear = true));
6452         static assert(!__traits(compiles, idate.isLeapYear = true));
6453     }
6454 
6455 
6456     /++
6457         Day of the week this $(LREF Date) is on.
6458       +/
dayOfWeek()6459     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
6460     {
6461         return getDayOfWeek(dayOfGregorianCal);
6462     }
6463 
6464     @safe unittest
6465     {
6466         const cdate = Date(1999, 7, 6);
6467         immutable idate = Date(1999, 7, 6);
6468         assert(cdate.dayOfWeek == DayOfWeek.tue);
6469         static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
6470         assert(idate.dayOfWeek == DayOfWeek.tue);
6471         static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
6472     }
6473 
6474 
6475     /++
6476         Day of the year this $(LREF Date) is on.
6477       +/
dayOfYear()6478     @property ushort dayOfYear() const @safe pure nothrow @nogc
6479     {
6480         if (_month >= Month.jan && _month <= Month.dec)
6481         {
6482             immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6483             auto monthIndex = _month - Month.jan;
6484 
6485             return cast(ushort)(lastDay[monthIndex] + _day);
6486         }
6487         assert(0, "Invalid month.");
6488     }
6489 
6490     ///
6491     @safe unittest
6492     {
6493         assert(Date(1999, 1, 1).dayOfYear == 1);
6494         assert(Date(1999, 12, 31).dayOfYear == 365);
6495         assert(Date(2000, 12, 31).dayOfYear == 366);
6496     }
6497 
6498     @safe unittest
6499     {
6500         import std.algorithm.iteration : filter;
6501         import std.range : chain;
6502 
6503         foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6504         {
6505             foreach (doy; testDaysOfYear)
6506                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6507         }
6508 
6509         foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6510         {
6511             foreach (doy; testDaysOfLeapYear)
6512                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6513         }
6514 
6515         const cdate = Date(1999, 7, 6);
6516         immutable idate = Date(1999, 7, 6);
6517         assert(cdate.dayOfYear == 187);
6518         assert(idate.dayOfYear == 187);
6519     }
6520 
6521     /++
6522         Day of the year.
6523 
6524         Params:
6525             day = The day of the year to set which day of the year this
6526                   $(LREF Date) is on.
6527 
6528         Throws:
6529             $(REF DateTimeException,std,datetime,date) if the given day is an
6530             invalid day of the year.
6531       +/
dayOfYear(int day)6532     @property void dayOfYear(int day) @safe pure
6533     {
6534         setDayOfYear!true(day);
6535     }
6536 
6537     private void setDayOfYear(bool useExceptions = false)(int day)
6538     {
6539         immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6540 
6541         bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
6542         enum errorMsg = "Invalid day of the year.";
6543 
6544         static if (useExceptions)
6545         {
6546             if (dayOutOfRange) throw new DateTimeException(errorMsg);
6547         }
6548         else
6549         {
6550             assert(!dayOutOfRange, errorMsg);
6551         }
6552 
6553         foreach (i; 1 .. lastDay.length)
6554         {
6555             if (day <= lastDay[i])
6556             {
6557                 _month = cast(Month)(cast(int) Month.jan + i - 1);
6558                 _day = cast(ubyte)(day - lastDay[i - 1]);
6559                 return;
6560             }
6561         }
6562         assert(0, "Invalid day of the year.");
6563     }
6564 
6565     @safe unittest
6566     {
6567         static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
6568         {
6569             date.dayOfYear = day;
6570             assert(date.month == expected.month);
6571             assert(date.day == expected.day);
6572         }
6573 
foreach(doy;testDaysOfYear)6574         foreach (doy; testDaysOfYear)
6575         {
6576             test(Date(1999, 1, 1), doy.day, doy.md);
6577             test(Date(-1, 1, 1), doy.day, doy.md);
6578         }
6579 
foreach(doy;testDaysOfLeapYear)6580         foreach (doy; testDaysOfLeapYear)
6581         {
6582             test(Date(2000, 1, 1), doy.day, doy.md);
6583             test(Date(-4, 1, 1), doy.day, doy.md);
6584         }
6585 
6586         const cdate = Date(1999, 7, 6);
6587         immutable idate = Date(1999, 7, 6);
6588         static assert(!__traits(compiles, cdate.dayOfYear = 187));
6589         static assert(!__traits(compiles, idate.dayOfYear = 187));
6590     }
6591 
6592 
6593     /++
6594         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6595      +/
dayOfGregorianCal()6596     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
6597     {
6598         if (isAD)
6599         {
6600             if (_year == 1)
6601                 return dayOfYear;
6602 
6603             int years = _year - 1;
6604             auto days = (years / 400) * daysIn400Years;
6605             years %= 400;
6606 
6607             days += (years / 100) * daysIn100Years;
6608             years %= 100;
6609 
6610             days += (years / 4) * daysIn4Years;
6611             years %= 4;
6612 
6613             days += years * daysInYear;
6614 
6615             days += dayOfYear;
6616 
6617             return days;
6618         }
6619         else if (_year == 0)
6620             return dayOfYear - daysInLeapYear;
6621         else
6622         {
6623             int years = _year;
6624             auto days = (years / 400) * daysIn400Years;
6625             years %= 400;
6626 
6627             days += (years / 100) * daysIn100Years;
6628             years %= 100;
6629 
6630             days += (years / 4) * daysIn4Years;
6631             years %= 4;
6632 
6633             if (years < 0)
6634             {
6635                 days -= daysInLeapYear;
6636                 ++years;
6637 
6638                 days += years * daysInYear;
6639 
6640                 days -= daysInYear - dayOfYear;
6641             }
6642             else
6643                 days -= daysInLeapYear - dayOfYear;
6644 
6645             return days;
6646         }
6647     }
6648 
6649     ///
6650     @safe unittest
6651     {
6652         assert(Date(1, 1, 1).dayOfGregorianCal == 1);
6653         assert(Date(1, 12, 31).dayOfGregorianCal == 365);
6654         assert(Date(2, 1, 1).dayOfGregorianCal == 366);
6655 
6656         assert(Date(0, 12, 31).dayOfGregorianCal == 0);
6657         assert(Date(0, 1, 1).dayOfGregorianCal == -365);
6658         assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
6659 
6660         assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
6661         assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
6662     }
6663 
6664     @safe unittest
6665     {
6666         import std.range : chain;
6667 
6668         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
6669             assert(gd.date.dayOfGregorianCal == gd.day);
6670 
6671         auto date = Date(1999, 7, 6);
6672         const cdate = Date(1999, 7, 6);
6673         immutable idate = Date(1999, 7, 6);
6674         assert(date.dayOfGregorianCal == 729_941);
6675         assert(cdate.dayOfGregorianCal == 729_941);
6676         assert(idate.dayOfGregorianCal == 729_941);
6677     }
6678 
6679     /++
6680         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6681 
6682         Params:
6683             day = The day of the Gregorian Calendar to set this $(LREF Date) to.
6684      +/
dayOfGregorianCal(int day)6685     @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
6686     {
6687         this = Date(day);
6688     }
6689 
6690     ///
6691     @safe unittest
6692     {
6693         auto date = Date.init;
6694         date.dayOfGregorianCal = 1;
6695         assert(date == Date(1, 1, 1));
6696 
6697         date.dayOfGregorianCal = 365;
6698         assert(date == Date(1, 12, 31));
6699 
6700         date.dayOfGregorianCal = 366;
6701         assert(date == Date(2, 1, 1));
6702 
6703         date.dayOfGregorianCal = 0;
6704         assert(date == Date(0, 12, 31));
6705 
6706         date.dayOfGregorianCal = -365;
6707         assert(date == Date(-0, 1, 1));
6708 
6709         date.dayOfGregorianCal = -366;
6710         assert(date == Date(-1, 12, 31));
6711 
6712         date.dayOfGregorianCal = 730_120;
6713         assert(date == Date(2000, 1, 1));
6714 
6715         date.dayOfGregorianCal = 734_137;
6716         assert(date == Date(2010, 12, 31));
6717     }
6718 
6719     @safe unittest
6720     {
6721         auto date = Date(1999, 7, 6);
6722         const cdate = Date(1999, 7, 6);
6723         immutable idate = Date(1999, 7, 6);
6724         date.dayOfGregorianCal = 187;
6725         assert(date.dayOfGregorianCal == 187);
6726         static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
6727         static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
6728     }
6729 
6730 
6731     /++
6732         The ISO 8601 week of the year that this $(LREF Date) is in.
6733 
6734         See_Also:
6735             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6736       +/
isoWeek()6737     @property ubyte isoWeek() const @safe pure nothrow
6738     {
6739         immutable weekday = dayOfWeek;
6740         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
6741         immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
6742 
6743         try
6744         {
6745             if (week == 53)
6746             {
6747                 switch (Date(_year + 1, 1, 1).dayOfWeek)
6748                 {
6749                     case DayOfWeek.mon:
6750                     case DayOfWeek.tue:
6751                     case DayOfWeek.wed:
6752                     case DayOfWeek.thu:
6753                         return 1;
6754                     case DayOfWeek.fri:
6755                     case DayOfWeek.sat:
6756                     case DayOfWeek.sun:
6757                         return 53;
6758                     default:
6759                         assert(0, "Invalid ISO Week");
6760                 }
6761             }
6762             else if (week > 0)
6763                 return cast(ubyte) week;
6764             else
6765                 return Date(_year - 1, 12, 31).isoWeek;
6766         }
6767         catch (Exception e)
6768             assert(0, "Date's constructor threw.");
6769     }
6770 
6771     @safe unittest
6772     {
6773         // Test A.D.
6774         assert(Date(2009, 12, 28).isoWeek == 53);
6775         assert(Date(2009, 12, 29).isoWeek == 53);
6776         assert(Date(2009, 12, 30).isoWeek == 53);
6777         assert(Date(2009, 12, 31).isoWeek == 53);
6778         assert(Date(2010, 1, 1).isoWeek == 53);
6779         assert(Date(2010, 1, 2).isoWeek == 53);
6780         assert(Date(2010, 1, 3).isoWeek == 53);
6781         assert(Date(2010, 1, 4).isoWeek == 1);
6782         assert(Date(2010, 1, 5).isoWeek == 1);
6783         assert(Date(2010, 1, 6).isoWeek == 1);
6784         assert(Date(2010, 1, 7).isoWeek == 1);
6785         assert(Date(2010, 1, 8).isoWeek == 1);
6786         assert(Date(2010, 1, 9).isoWeek == 1);
6787         assert(Date(2010, 1, 10).isoWeek == 1);
6788         assert(Date(2010, 1, 11).isoWeek == 2);
6789         assert(Date(2010, 12, 31).isoWeek == 52);
6790 
6791         assert(Date(2004, 12, 26).isoWeek == 52);
6792         assert(Date(2004, 12, 27).isoWeek == 53);
6793         assert(Date(2004, 12, 28).isoWeek == 53);
6794         assert(Date(2004, 12, 29).isoWeek == 53);
6795         assert(Date(2004, 12, 30).isoWeek == 53);
6796         assert(Date(2004, 12, 31).isoWeek == 53);
6797         assert(Date(2005, 1, 1).isoWeek == 53);
6798         assert(Date(2005, 1, 2).isoWeek == 53);
6799 
6800         assert(Date(2005, 12, 31).isoWeek == 52);
6801         assert(Date(2007, 1, 1).isoWeek == 1);
6802 
6803         assert(Date(2007, 12, 30).isoWeek == 52);
6804         assert(Date(2007, 12, 31).isoWeek == 1);
6805         assert(Date(2008, 1, 1).isoWeek == 1);
6806 
6807         assert(Date(2008, 12, 28).isoWeek == 52);
6808         assert(Date(2008, 12, 29).isoWeek == 1);
6809         assert(Date(2008, 12, 30).isoWeek == 1);
6810         assert(Date(2008, 12, 31).isoWeek == 1);
6811         assert(Date(2009, 1, 1).isoWeek == 1);
6812         assert(Date(2009, 1, 2).isoWeek == 1);
6813         assert(Date(2009, 1, 3).isoWeek == 1);
6814         assert(Date(2009, 1, 4).isoWeek == 1);
6815 
6816         // Test B.C.
6817         // The algorithm should work identically for both A.D. and B.C. since
6818         // it doesn't really take the year into account, so B.C. testing
6819         // probably isn't really needed.
6820         assert(Date(0, 12, 31).isoWeek == 52);
6821         assert(Date(0, 1, 4).isoWeek == 1);
6822         assert(Date(0, 1, 1).isoWeek == 52);
6823 
6824         const cdate = Date(1999, 7, 6);
6825         immutable idate = Date(1999, 7, 6);
6826         assert(cdate.isoWeek == 27);
6827         static assert(!__traits(compiles, cdate.isoWeek = 3));
6828         assert(idate.isoWeek == 27);
6829         static assert(!__traits(compiles, idate.isoWeek = 3));
6830     }
6831 
6832 
6833     /++
6834         $(LREF Date) for the last day in the month that this $(LREF Date) is in.
6835       +/
endOfMonth()6836     @property Date endOfMonth() const @safe pure nothrow
6837     {
6838         try
6839             return Date(_year, _month, maxDay(_year, _month));
6840         catch (Exception e)
6841             assert(0, "Date's constructor threw.");
6842     }
6843 
6844     ///
6845     @safe unittest
6846     {
6847         assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
6848         assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
6849         assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
6850         assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
6851     }
6852 
6853     @safe unittest
6854     {
6855         // Test A.D.
6856         assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
6857         assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
6858         assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
6859         assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
6860         assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
6861         assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
6862         assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
6863         assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
6864         assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
6865         assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
6866         assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
6867         assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
6868         assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
6869 
6870         // Test B.C.
6871         assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
6872         assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
6873         assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
6874         assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
6875         assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
6876         assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
6877         assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
6878         assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
6879         assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
6880         assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
6881         assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
6882         assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
6883         assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
6884 
6885         const cdate = Date(1999, 7, 6);
6886         immutable idate = Date(1999, 7, 6);
6887         static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
6888         static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
6889     }
6890 
6891 
6892     /++
6893         The last day in the month that this $(LREF Date) is in.
6894       +/
daysInMonth()6895     @property ubyte daysInMonth() const @safe pure nothrow @nogc
6896     {
6897         return maxDay(_year, _month);
6898     }
6899 
6900     ///
6901     @safe unittest
6902     {
6903         assert(Date(1999, 1, 6).daysInMonth == 31);
6904         assert(Date(1999, 2, 7).daysInMonth == 28);
6905         assert(Date(2000, 2, 7).daysInMonth == 29);
6906         assert(Date(2000, 6, 4).daysInMonth == 30);
6907     }
6908 
6909     @safe unittest
6910     {
6911         // Test A.D.
6912         assert(Date(1999, 1, 1).daysInMonth == 31);
6913         assert(Date(1999, 2, 1).daysInMonth == 28);
6914         assert(Date(2000, 2, 1).daysInMonth == 29);
6915         assert(Date(1999, 3, 1).daysInMonth == 31);
6916         assert(Date(1999, 4, 1).daysInMonth == 30);
6917         assert(Date(1999, 5, 1).daysInMonth == 31);
6918         assert(Date(1999, 6, 1).daysInMonth == 30);
6919         assert(Date(1999, 7, 1).daysInMonth == 31);
6920         assert(Date(1999, 8, 1).daysInMonth == 31);
6921         assert(Date(1999, 9, 1).daysInMonth == 30);
6922         assert(Date(1999, 10, 1).daysInMonth == 31);
6923         assert(Date(1999, 11, 1).daysInMonth == 30);
6924         assert(Date(1999, 12, 1).daysInMonth == 31);
6925 
6926         // Test B.C.
6927         assert(Date(-1999, 1, 1).daysInMonth == 31);
6928         assert(Date(-1999, 2, 1).daysInMonth == 28);
6929         assert(Date(-2000, 2, 1).daysInMonth == 29);
6930         assert(Date(-1999, 3, 1).daysInMonth == 31);
6931         assert(Date(-1999, 4, 1).daysInMonth == 30);
6932         assert(Date(-1999, 5, 1).daysInMonth == 31);
6933         assert(Date(-1999, 6, 1).daysInMonth == 30);
6934         assert(Date(-1999, 7, 1).daysInMonth == 31);
6935         assert(Date(-1999, 8, 1).daysInMonth == 31);
6936         assert(Date(-1999, 9, 1).daysInMonth == 30);
6937         assert(Date(-1999, 10, 1).daysInMonth == 31);
6938         assert(Date(-1999, 11, 1).daysInMonth == 30);
6939         assert(Date(-1999, 12, 1).daysInMonth == 31);
6940 
6941         const cdate = Date(1999, 7, 6);
6942         immutable idate = Date(1999, 7, 6);
6943         static assert(!__traits(compiles, cdate.daysInMonth = 30));
6944         static assert(!__traits(compiles, idate.daysInMonth = 30));
6945     }
6946 
6947 
6948     /++
6949         Whether the current year is a date in A.D.
6950       +/
isAD()6951     @property bool isAD() const @safe pure nothrow @nogc
6952     {
6953         return _year > 0;
6954     }
6955 
6956     ///
6957     @safe unittest
6958     {
6959         assert(Date(1, 1, 1).isAD);
6960         assert(Date(2010, 12, 31).isAD);
6961         assert(!Date(0, 12, 31).isAD);
6962         assert(!Date(-2010, 1, 1).isAD);
6963     }
6964 
6965     @safe unittest
6966     {
6967         assert(Date(2010, 7, 4).isAD);
6968         assert(Date(1, 1, 1).isAD);
6969         assert(!Date(0, 1, 1).isAD);
6970         assert(!Date(-1, 1, 1).isAD);
6971         assert(!Date(-2010, 7, 4).isAD);
6972 
6973         const cdate = Date(1999, 7, 6);
6974         immutable idate = Date(1999, 7, 6);
6975         assert(cdate.isAD);
6976         assert(idate.isAD);
6977     }
6978 
6979 
6980     /++
6981         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
6982         $(LREF Date) at noon (since the Julian day changes at noon).
6983       +/
julianDay()6984     @property long julianDay() const @safe pure nothrow @nogc
6985     {
6986         return dayOfGregorianCal + 1_721_425;
6987     }
6988 
6989     @safe unittest
6990     {
6991         assert(Date(-4713, 11, 24).julianDay == 0);
6992         assert(Date(0, 12, 31).julianDay == 1_721_425);
6993         assert(Date(1, 1, 1).julianDay == 1_721_426);
6994         assert(Date(1582, 10, 15).julianDay == 2_299_161);
6995         assert(Date(1858, 11, 17).julianDay == 2_400_001);
6996         assert(Date(1982, 1, 4).julianDay == 2_444_974);
6997         assert(Date(1996, 3, 31).julianDay == 2_450_174);
6998         assert(Date(2010, 8, 24).julianDay == 2_455_433);
6999 
7000         const cdate = Date(1999, 7, 6);
7001         immutable idate = Date(1999, 7, 6);
7002         assert(cdate.julianDay == 2_451_366);
7003         assert(idate.julianDay == 2_451_366);
7004     }
7005 
7006 
7007     /++
7008         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
7009         any time on this date (since, the modified Julian day changes at
7010         midnight).
7011       +/
modJulianDay()7012     @property long modJulianDay() const @safe pure nothrow @nogc
7013     {
7014         return julianDay - 2_400_001;
7015     }
7016 
7017     @safe unittest
7018     {
7019         assert(Date(1858, 11, 17).modJulianDay == 0);
7020         assert(Date(2010, 8, 24).modJulianDay == 55_432);
7021 
7022         const cdate = Date(1999, 7, 6);
7023         immutable idate = Date(1999, 7, 6);
7024         assert(cdate.modJulianDay == 51_365);
7025         assert(idate.modJulianDay == 51_365);
7026     }
7027 
7028 
7029     /++
7030         Converts this $(LREF Date) to a string with the format YYYYMMDD.
7031       +/
toISOString()7032     string toISOString() const @safe pure nothrow
7033     {
7034         import std.format : format;
7035         try
7036         {
7037             if (_year >= 0)
7038             {
7039                 if (_year < 10_000)
7040                     return format("%04d%02d%02d", _year, _month, _day);
7041                 else
7042                     return format("+%05d%02d%02d", _year, _month, _day);
7043             }
7044             else if (_year > -10_000)
7045                 return format("%05d%02d%02d", _year, _month, _day);
7046             else
7047                 return format("%06d%02d%02d", _year, _month, _day);
7048         }
7049         catch (Exception e)
7050             assert(0, "format() threw.");
7051     }
7052 
7053     ///
7054     @safe unittest
7055     {
7056         assert(Date(2010, 7, 4).toISOString() == "20100704");
7057         assert(Date(1998, 12, 25).toISOString() == "19981225");
7058         assert(Date(0, 1, 5).toISOString() == "00000105");
7059         assert(Date(-4, 1, 5).toISOString() == "-00040105");
7060     }
7061 
7062     @safe unittest
7063     {
7064         // Test A.D.
7065         assert(Date(9, 12, 4).toISOString() == "00091204");
7066         assert(Date(99, 12, 4).toISOString() == "00991204");
7067         assert(Date(999, 12, 4).toISOString() == "09991204");
7068         assert(Date(9999, 7, 4).toISOString() == "99990704");
7069         assert(Date(10000, 10, 20).toISOString() == "+100001020");
7070 
7071         // Test B.C.
7072         assert(Date(0, 12, 4).toISOString() == "00001204");
7073         assert(Date(-9, 12, 4).toISOString() == "-00091204");
7074         assert(Date(-99, 12, 4).toISOString() == "-00991204");
7075         assert(Date(-999, 12, 4).toISOString() == "-09991204");
7076         assert(Date(-9999, 7, 4).toISOString() == "-99990704");
7077         assert(Date(-10000, 10, 20).toISOString() == "-100001020");
7078 
7079         const cdate = Date(1999, 7, 6);
7080         immutable idate = Date(1999, 7, 6);
7081         assert(cdate.toISOString() == "19990706");
7082         assert(idate.toISOString() == "19990706");
7083     }
7084 
7085     /++
7086         Converts this $(LREF Date) to a string with the format YYYY-MM-DD.
7087       +/
toISOExtString()7088     string toISOExtString() const @safe pure nothrow
7089     {
7090         import std.format : format;
7091         try
7092         {
7093             if (_year >= 0)
7094             {
7095                 if (_year < 10_000)
7096                     return format("%04d-%02d-%02d", _year, _month, _day);
7097                 else
7098                     return format("+%05d-%02d-%02d", _year, _month, _day);
7099             }
7100             else if (_year > -10_000)
7101                 return format("%05d-%02d-%02d", _year, _month, _day);
7102             else
7103                 return format("%06d-%02d-%02d", _year, _month, _day);
7104         }
7105         catch (Exception e)
7106             assert(0, "format() threw.");
7107     }
7108 
7109     ///
7110     @safe unittest
7111     {
7112         assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
7113         assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
7114         assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
7115         assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
7116     }
7117 
7118     @safe unittest
7119     {
7120         // Test A.D.
7121         assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
7122         assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
7123         assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
7124         assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
7125         assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
7126 
7127         // Test B.C.
7128         assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
7129         assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
7130         assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
7131         assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
7132         assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
7133         assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
7134 
7135         const cdate = Date(1999, 7, 6);
7136         immutable idate = Date(1999, 7, 6);
7137         assert(cdate.toISOExtString() == "1999-07-06");
7138         assert(idate.toISOExtString() == "1999-07-06");
7139     }
7140 
7141     /++
7142         Converts this $(LREF Date) to a string with the format YYYY-Mon-DD.
7143       +/
toSimpleString()7144     string toSimpleString() const @safe pure nothrow
7145     {
7146         import std.format : format;
7147         try
7148         {
7149             if (_year >= 0)
7150             {
7151                 if (_year < 10_000)
7152                     return format("%04d-%s-%02d", _year, monthToString(_month), _day);
7153                 else
7154                     return format("+%05d-%s-%02d", _year, monthToString(_month), _day);
7155             }
7156             else if (_year > -10_000)
7157                 return format("%05d-%s-%02d", _year, monthToString(_month), _day);
7158             else
7159                 return format("%06d-%s-%02d", _year, monthToString(_month), _day);
7160         }
7161         catch (Exception e)
7162             assert(0, "format() threw.");
7163     }
7164 
7165     ///
7166     @safe unittest
7167     {
7168         assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
7169         assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
7170         assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
7171         assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
7172     }
7173 
7174     @safe unittest
7175     {
7176         // Test A.D.
7177         assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
7178         assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
7179         assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
7180         assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
7181         assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
7182 
7183         // Test B.C.
7184         assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
7185         assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
7186         assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
7187         assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
7188         assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
7189         assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
7190 
7191         const cdate = Date(1999, 7, 6);
7192         immutable idate = Date(1999, 7, 6);
7193         assert(cdate.toSimpleString() == "1999-Jul-06");
7194         assert(idate.toSimpleString() == "1999-Jul-06");
7195     }
7196 
7197 
7198     /++
7199         Converts this $(LREF Date) to a string.
7200 
7201         This function exists to make it easy to convert a $(LREF Date) to a
7202         string for code that does not care what the exact format is - just that
7203         it presents the information in a clear manner. It also makes it easy to
7204         simply convert a $(LREF Date) to a string when using functions such as
7205         `to!string`, `format`, or `writeln` which use toString to convert
7206         user-defined types. So, it is unlikely that much code will call
7207         toString directly.
7208 
7209         The format of the string is purposefully unspecified, and code that
7210         cares about the format of the string should use `toISOString`,
7211         `toISOExtString`, `toSimpleString`, or some other custom formatting
7212         function that explicitly generates the format that the code needs. The
7213         reason is that the code is then clear about what format it's using,
7214         making it less error-prone to maintain the code and interact with other
7215         software that consumes the generated strings. It's for this same reason
7216         $(LREF Date) has no `fromString` function, whereas it does have
7217         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
7218 
7219         The format returned by toString may or may not change in the future.
7220       +/
toString()7221     string toString() const @safe pure nothrow
7222     {
7223         return toSimpleString();
7224     }
7225 
7226     @safe unittest
7227     {
7228         auto date = Date(1999, 7, 6);
7229         const cdate = Date(1999, 7, 6);
7230         immutable idate = Date(1999, 7, 6);
7231         assert(date.toString());
7232         assert(cdate.toString());
7233         assert(idate.toString());
7234     }
7235 
7236 
7237     /++
7238         Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
7239         is stripped from the given string.
7240 
7241         Params:
7242             isoString = A string formatted in the ISO format for dates.
7243 
7244         Throws:
7245             $(REF DateTimeException,std,datetime,date) if the given string is
7246             not in the ISO format or if the resulting $(LREF Date) would not be
7247             valid.
7248       +/
7249     static Date fromISOString(S)(in S isoString) @safe pure
7250         if (isSomeString!S)
7251     {
7252         import std.algorithm.searching : startsWith;
7253         import std.conv : to, text, ConvException;
7254         import std.exception : enforce;
7255         import std.string : strip;
7256 
7257         auto str = isoString.strip;
7258 
7259         enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString));
7260 
7261         int day, month, year;
7262         auto yearStr = str[0 .. $ - 4];
7263 
7264         try
7265         {
7266             // using conversion to uint plus cast because it checks for +/-
7267             // for us quickly while throwing ConvException
7268             day = cast(int) to!uint(str[$ - 2 .. $]);
7269             month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
7270 
7271             if (yearStr.length > 4)
7272             {
7273                 enforce!DateTimeException(yearStr.startsWith('-', '+'),
7274                         text("Invalid ISO String: ", isoString));
7275                 year = to!int(yearStr);
7276             }
7277             else
7278             {
7279                 year = cast(int) to!uint(yearStr);
7280             }
7281         }
catch(ConvException)7282         catch (ConvException)
7283         {
7284             throw new DateTimeException(text("Invalid ISO String: ", isoString));
7285         }
7286 
7287         return Date(year, month, day);
7288     }
7289 
7290     ///
7291     @safe unittest
7292     {
7293         assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
7294         assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
7295         assert(Date.fromISOString("00000105") == Date(0, 1, 5));
7296         assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
7297         assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
7298     }
7299 
7300     @safe unittest
7301     {
7302         assertThrown!DateTimeException(Date.fromISOString(""));
7303         assertThrown!DateTimeException(Date.fromISOString("990704"));
7304         assertThrown!DateTimeException(Date.fromISOString("0100704"));
7305         assertThrown!DateTimeException(Date.fromISOString("2010070"));
7306         assertThrown!DateTimeException(Date.fromISOString("2010070 "));
7307         assertThrown!DateTimeException(Date.fromISOString("120100704"));
7308         assertThrown!DateTimeException(Date.fromISOString("-0100704"));
7309         assertThrown!DateTimeException(Date.fromISOString("+0100704"));
7310         assertThrown!DateTimeException(Date.fromISOString("2010070a"));
7311         assertThrown!DateTimeException(Date.fromISOString("20100a04"));
7312         assertThrown!DateTimeException(Date.fromISOString("2010a704"));
7313 
7314         assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
7315         assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
7316         assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
7317         assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
7318         assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
7319         assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
7320         assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
7321         assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
7322         assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
7323         assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
7324         assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
7325         assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
7326         assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
7327         assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
7328         assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
7329         assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
7330         assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
7331 
7332         assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
7333         assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
7334         assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
7335         assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
7336         assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
7337         assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
7338         assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
7339         assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
7340         assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
7341         assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
7342 
7343         assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
7344         assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
7345         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
7346         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
7347         assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
7348         assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
7349         assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
7350         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
7351         assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
7352         assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
7353         assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
7354 
7355         assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
7356         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
7357 
7358         assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
7359         assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
7360         assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
7361         assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
7362         assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
7363         assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
7364     }
7365 
7366     // bug# 17801
7367     @safe unittest
7368     {
7369         import std.conv : to;
7370         import std.meta : AliasSeq;
7371         foreach (C; AliasSeq!(char, wchar, dchar))
7372         {
7373             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7374                 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
7375         }
7376     }
7377 
7378 
7379     /++
7380         Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
7381         Whitespace is stripped from the given string.
7382 
7383         Params:
7384             isoExtString = A string formatted in the ISO Extended format for
7385                            dates.
7386 
7387         Throws:
7388             $(REF DateTimeException,std,datetime,date) if the given string is
7389             not in the ISO Extended format or if the resulting $(LREF Date)
7390             would not be valid.
7391       +/
7392     static Date fromISOExtString(S)(in S isoExtString) @safe pure
7393         if (isSomeString!(S))
7394     {
7395         import std.algorithm.searching : all, startsWith;
7396         import std.ascii : isDigit;
7397         import std.conv : to;
7398         import std.exception : enforce;
7399         import std.format : format;
7400         import std.string : strip;
7401 
7402         auto dstr = to!dstring(strip(isoExtString));
7403 
7404         enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7405 
7406         auto day = dstr[$-2 .. $];
7407         auto month = dstr[$-5 .. $-3];
7408         auto year = dstr[0 .. $-6];
7409 
7410         enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7411         enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7412         enforce(all!isDigit(day),
7413                 new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7414         enforce(all!isDigit(month),
7415                 new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7416 
7417         if (year.length > 4)
7418         {
7419             enforce(year.startsWith('-', '+'),
7420                     new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7421             enforce(all!isDigit(year[1..$]),
7422                     new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7423         }
7424         else
7425             enforce(all!isDigit(year),
7426                     new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
7427 
7428         return Date(to!short(year), to!ubyte(month), to!ubyte(day));
7429     }
7430 
7431     ///
7432     @safe unittest
7433     {
7434         assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
7435         assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
7436         assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
7437         assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
7438         assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
7439     }
7440 
7441     @safe unittest
7442     {
7443         assertThrown!DateTimeException(Date.fromISOExtString(""));
7444         assertThrown!DateTimeException(Date.fromISOExtString("990704"));
7445         assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
7446         assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
7447         assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
7448         assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
7449         assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
7450         assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
7451         assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
7452         assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
7453         assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
7454 
7455         assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
7456         assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
7457         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
7458         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
7459         assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
7460         assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
7461         assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
7462         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
7463         assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
7464         assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
7465         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
7466         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
7467         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
7468         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
7469         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
7470         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
7471         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
7472 
7473         assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
7474         assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
7475         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
7476         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
7477         assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
7478         assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
7479         assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
7480         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
7481         assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
7482         assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
7483 
7484         assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
7485         assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
7486         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
7487         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
7488         assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
7489         assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
7490         assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
7491         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
7492         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
7493         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
7494         assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
7495 
7496         assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
7497         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
7498 
7499         assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
7500         assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
7501         assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
7502         assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
7503         assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
7504         assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
7505     }
7506 
7507     // bug# 17801
7508     @safe unittest
7509     {
7510         import std.conv : to;
7511         import std.meta : AliasSeq;
7512         foreach (C; AliasSeq!(char, wchar, dchar))
7513         {
7514             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7515                 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
7516         }
7517     }
7518 
7519 
7520     /++
7521         Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
7522         Whitespace is stripped from the given string.
7523 
7524         Params:
7525             simpleString = A string formatted in the way that toSimpleString
7526                            formats dates.
7527 
7528         Throws:
7529             $(REF DateTimeException,std,datetime,date) if the given string is
7530             not in the correct format or if the resulting $(LREF Date) would not
7531             be valid.
7532       +/
7533     static Date fromSimpleString(S)(in S simpleString) @safe pure
7534         if (isSomeString!(S))
7535     {
7536         import std.algorithm.searching : all, startsWith;
7537         import std.ascii : isDigit;
7538         import std.conv : to;
7539         import std.exception : enforce;
7540         import std.format : format;
7541         import std.string : strip;
7542 
7543         auto dstr = to!dstring(strip(simpleString));
7544 
7545         enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString)));
7546 
7547         auto day = dstr[$-2 .. $];
7548         auto month = monthFromString(to!string(dstr[$-6 .. $-3]));
7549         auto year = dstr[0 .. $-7];
7550 
7551         enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString)));
7552         enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString)));
7553         enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString)));
7554 
7555         if (year.length > 4)
7556         {
7557             enforce(year.startsWith('-', '+'),
7558                     new DateTimeException(format("Invalid string format: %s", simpleString)));
7559             enforce(all!isDigit(year[1..$]),
7560                     new DateTimeException(format("Invalid string format: %s", simpleString)));
7561         }
7562         else
7563             enforce(all!isDigit(year),
7564                     new DateTimeException(format("Invalid string format: %s", simpleString)));
7565 
7566         return Date(to!short(year), month, to!ubyte(day));
7567     }
7568 
7569     ///
7570     @safe unittest
7571     {
7572         assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
7573         assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
7574         assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
7575         assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
7576         assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
7577     }
7578 
7579     @safe unittest
7580     {
7581         assertThrown!DateTimeException(Date.fromSimpleString(""));
7582         assertThrown!DateTimeException(Date.fromSimpleString("990704"));
7583         assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
7584         assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
7585         assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
7586         assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
7587         assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
7588         assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
7589         assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
7590         assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
7591         assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
7592 
7593         assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
7594         assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
7595         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
7596         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
7597         assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
7598         assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
7599         assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
7600         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
7601         assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
7602         assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
7603         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
7604         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
7605         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
7606         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
7607         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
7608         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
7609         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
7610 
7611         assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
7612         assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
7613         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
7614         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
7615         assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
7616         assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
7617         assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
7618         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
7619         assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
7620         assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
7621 
7622         assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
7623         assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
7624         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
7625         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
7626         assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
7627         assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
7628         assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
7629         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
7630         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
7631         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
7632         assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
7633 
7634         assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
7635         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
7636 
7637         assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
7638         assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
7639         assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
7640         assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
7641         assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
7642         assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
7643     }
7644 
7645     // bug# 17801
7646     @safe unittest
7647     {
7648         import std.conv : to;
7649         import std.meta : AliasSeq;
7650         foreach (C; AliasSeq!(char, wchar, dchar))
7651         {
7652             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7653                 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
7654         }
7655     }
7656 
7657 
7658     /++
7659         Returns the $(LREF Date) farthest in the past which is representable by
7660         $(LREF Date).
7661       +/
min()7662     @property static Date min() @safe pure nothrow @nogc
7663     {
7664         auto date = Date.init;
7665         date._year = short.min;
7666         date._month = Month.jan;
7667         date._day = 1;
7668 
7669         return date;
7670     }
7671 
7672     @safe unittest
7673     {
7674         assert(Date.min.year < 0);
7675         assert(Date.min < Date.max);
7676     }
7677 
7678 
7679     /++
7680         Returns the $(LREF Date) farthest in the future which is representable
7681         by $(LREF Date).
7682       +/
max()7683     @property static Date max() @safe pure nothrow @nogc
7684     {
7685         auto date = Date.init;
7686         date._year = short.max;
7687         date._month = Month.dec;
7688         date._day = 31;
7689 
7690         return date;
7691     }
7692 
7693     @safe unittest
7694     {
7695         assert(Date.max.year > 0);
7696         assert(Date.max > Date.min);
7697     }
7698 
7699 
7700 private:
7701 
7702     /+
7703         Whether the given values form a valid date.
7704 
7705         Params:
7706             year  = The year to test.
7707             month = The month of the Gregorian Calendar to test.
7708             day   = The day of the month to test.
7709      +/
_valid(int year,int month,int day)7710     static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
7711     {
7712         if (!valid!"months"(month))
7713             return false;
7714         return valid!"days"(year, month, day);
7715     }
7716 
7717 
7718 package:
7719 
7720     /+
7721         Adds the given number of days to this $(LREF Date). A negative number
7722         will subtract.
7723 
7724         The month will be adjusted along with the day if the number of days
7725         added (or subtracted) would overflow (or underflow) the current month.
7726         The year will be adjusted along with the month if the increase (or
7727         decrease) to the month would cause it to overflow (or underflow) the
7728         current year.
7729 
7730         $(D _addDays(numDays)) is effectively equivalent to
7731         $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
7732 
7733         Params:
7734             days = The number of days to add to this Date.
7735       +/
7736     ref Date _addDays(long days) return @safe pure nothrow @nogc
7737     {
7738         dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
7739         return this;
7740     }
7741 
7742     @safe unittest
7743     {
7744         // Test A.D.
7745         {
7746             auto date = Date(1999, 2, 28);
7747             date._addDays(1);
7748             assert(date == Date(1999, 3, 1));
7749             date._addDays(-1);
7750             assert(date == Date(1999, 2, 28));
7751         }
7752 
7753         {
7754             auto date = Date(2000, 2, 28);
7755             date._addDays(1);
7756             assert(date == Date(2000, 2, 29));
7757             date._addDays(1);
7758             assert(date == Date(2000, 3, 1));
7759             date._addDays(-1);
7760             assert(date == Date(2000, 2, 29));
7761         }
7762 
7763         {
7764             auto date = Date(1999, 6, 30);
7765             date._addDays(1);
7766             assert(date == Date(1999, 7, 1));
7767             date._addDays(-1);
7768             assert(date == Date(1999, 6, 30));
7769         }
7770 
7771         {
7772             auto date = Date(1999, 7, 31);
7773             date._addDays(1);
7774             assert(date == Date(1999, 8, 1));
7775             date._addDays(-1);
7776             assert(date == Date(1999, 7, 31));
7777         }
7778 
7779         {
7780             auto date = Date(1999, 1, 1);
7781             date._addDays(-1);
7782             assert(date == Date(1998, 12, 31));
7783             date._addDays(1);
7784             assert(date == Date(1999, 1, 1));
7785         }
7786 
7787         {
7788             auto date = Date(1999, 7, 6);
7789             date._addDays(9);
7790             assert(date == Date(1999, 7, 15));
7791             date._addDays(-11);
7792             assert(date == Date(1999, 7, 4));
7793             date._addDays(30);
7794             assert(date == Date(1999, 8, 3));
7795             date._addDays(-3);
7796             assert(date == Date(1999, 7, 31));
7797         }
7798 
7799         {
7800             auto date = Date(1999, 7, 6);
7801             date._addDays(365);
7802             assert(date == Date(2000, 7, 5));
7803             date._addDays(-365);
7804             assert(date == Date(1999, 7, 6));
7805             date._addDays(366);
7806             assert(date == Date(2000, 7, 6));
7807             date._addDays(730);
7808             assert(date == Date(2002, 7, 6));
7809             date._addDays(-1096);
7810             assert(date == Date(1999, 7, 6));
7811         }
7812 
7813         // Test B.C.
7814         {
7815             auto date = Date(-1999, 2, 28);
7816             date._addDays(1);
7817             assert(date == Date(-1999, 3, 1));
7818             date._addDays(-1);
7819             assert(date == Date(-1999, 2, 28));
7820         }
7821 
7822         {
7823             auto date = Date(-2000, 2, 28);
7824             date._addDays(1);
7825             assert(date == Date(-2000, 2, 29));
7826             date._addDays(1);
7827             assert(date == Date(-2000, 3, 1));
7828             date._addDays(-1);
7829             assert(date == Date(-2000, 2, 29));
7830         }
7831 
7832         {
7833             auto date = Date(-1999, 6, 30);
7834             date._addDays(1);
7835             assert(date == Date(-1999, 7, 1));
7836             date._addDays(-1);
7837             assert(date == Date(-1999, 6, 30));
7838         }
7839 
7840         {
7841             auto date = Date(-1999, 7, 31);
7842             date._addDays(1);
7843             assert(date == Date(-1999, 8, 1));
7844             date._addDays(-1);
7845             assert(date == Date(-1999, 7, 31));
7846         }
7847 
7848         {
7849             auto date = Date(-1999, 1, 1);
7850             date._addDays(-1);
7851             assert(date == Date(-2000, 12, 31));
7852             date._addDays(1);
7853             assert(date == Date(-1999, 1, 1));
7854         }
7855 
7856         {
7857             auto date = Date(-1999, 7, 6);
7858             date._addDays(9);
7859             assert(date == Date(-1999, 7, 15));
7860             date._addDays(-11);
7861             assert(date == Date(-1999, 7, 4));
7862             date._addDays(30);
7863             assert(date == Date(-1999, 8, 3));
7864             date._addDays(-3);
7865         }
7866 
7867         {
7868             auto date = Date(-1999, 7, 6);
7869             date._addDays(365);
7870             assert(date == Date(-1998, 7, 6));
7871             date._addDays(-365);
7872             assert(date == Date(-1999, 7, 6));
7873             date._addDays(366);
7874             assert(date == Date(-1998, 7, 7));
7875             date._addDays(730);
7876             assert(date == Date(-1996, 7, 6));
7877             date._addDays(-1096);
7878             assert(date == Date(-1999, 7, 6));
7879         }
7880 
7881         // Test Both
7882         {
7883             auto date = Date(1, 7, 6);
7884             date._addDays(-365);
7885             assert(date == Date(0, 7, 6));
7886             date._addDays(365);
7887             assert(date == Date(1, 7, 6));
7888             date._addDays(-731);
7889             assert(date == Date(-1, 7, 6));
7890             date._addDays(730);
7891             assert(date == Date(1, 7, 5));
7892         }
7893 
7894         const cdate = Date(1999, 7, 6);
7895         immutable idate = Date(1999, 7, 6);
7896         static assert(!__traits(compiles, cdate._addDays(12)));
7897         static assert(!__traits(compiles, idate._addDays(12)));
7898     }
7899 
7900 
7901     @safe pure invariant()
7902     {
7903         import std.format : format;
7904         assert(valid!"months"(_month),
7905                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
7906         assert(valid!"days"(_year, _month, _day),
7907                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
7908     }
7909 
7910     short _year  = 1;
7911     Month _month = Month.jan;
7912     ubyte _day   = 1;
7913 }
7914 
7915 
7916 /++
7917     Represents a time of day with hours, minutes, and seconds. It uses 24 hour
7918     time.
7919 +/
7920 struct TimeOfDay
7921 {
7922 public:
7923 
7924     /++
7925         Params:
7926             hour   = Hour of the day [0 - 24$(RPAREN).
7927             minute = Minute of the hour [0 - 60$(RPAREN).
7928             second = Second of the minute [0 - 60$(RPAREN).
7929 
7930         Throws:
7931             $(REF DateTimeException,std,datetime,date) if the resulting
7932             $(LREF TimeOfDay) would be not be valid.
7933      +/
7934     this(int hour, int minute, int second = 0) @safe pure
7935     {
7936         enforceValid!"hours"(hour);
7937         enforceValid!"minutes"(minute);
7938         enforceValid!"seconds"(second);
7939 
7940         _hour   = cast(ubyte) hour;
7941         _minute = cast(ubyte) minute;
7942         _second = cast(ubyte) second;
7943     }
7944 
7945     @safe unittest
7946     {
7947         assert(TimeOfDay(0, 0) == TimeOfDay.init);
7948 
7949         {
7950             auto tod = TimeOfDay(0, 0);
7951             assert(tod._hour == 0);
7952             assert(tod._minute == 0);
7953             assert(tod._second == 0);
7954         }
7955 
7956         {
7957             auto tod = TimeOfDay(12, 30, 33);
7958             assert(tod._hour == 12);
7959             assert(tod._minute == 30);
7960             assert(tod._second == 33);
7961         }
7962 
7963         {
7964             auto tod = TimeOfDay(23, 59, 59);
7965             assert(tod._hour == 23);
7966             assert(tod._minute == 59);
7967             assert(tod._second == 59);
7968         }
7969 
7970         assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
7971         assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
7972         assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
7973     }
7974 
7975 
7976     /++
7977         Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
7978 
7979         Returns:
7980             $(BOOKTABLE,
7981             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
7982             $(TR $(TD this == rhs) $(TD 0))
7983             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
7984             )
7985      +/
opCmpTimeOfDay7986     int opCmp(in TimeOfDay rhs) const @safe pure nothrow @nogc
7987     {
7988         if (_hour < rhs._hour)
7989             return -1;
7990         if (_hour > rhs._hour)
7991             return 1;
7992 
7993         if (_minute < rhs._minute)
7994             return -1;
7995         if (_minute > rhs._minute)
7996             return 1;
7997 
7998         if (_second < rhs._second)
7999             return -1;
8000         if (_second > rhs._second)
8001             return 1;
8002 
8003         return 0;
8004     }
8005 
8006     @safe unittest
8007     {
8008         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
8009 
8010         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
8011         assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
8012         assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
8013         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8014 
8015         assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
8016         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
8017 
8018         assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
8019         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8020 
8021         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8022         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8023         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
8024         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8025         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
8026         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
8027 
8028         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8029         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
8030         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
8031         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8032 
8033         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8034         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
8035 
8036         const ctod = TimeOfDay(12, 30, 33);
8037         immutable itod = TimeOfDay(12, 30, 33);
8038         assert(ctod.opCmp(itod) == 0);
8039         assert(itod.opCmp(ctod) == 0);
8040     }
8041 
8042 
8043     /++
8044         Hours past midnight.
8045      +/
hourTimeOfDay8046     @property ubyte hour() const @safe pure nothrow @nogc
8047     {
8048         return _hour;
8049     }
8050 
8051     @safe unittest
8052     {
8053         assert(TimeOfDay.init.hour == 0);
8054         assert(TimeOfDay(12, 0, 0).hour == 12);
8055 
8056         const ctod = TimeOfDay(12, 0, 0);
8057         immutable itod = TimeOfDay(12, 0, 0);
8058         assert(ctod.hour == 12);
8059         assert(itod.hour == 12);
8060     }
8061 
8062 
8063     /++
8064         Hours past midnight.
8065 
8066         Params:
8067             hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
8068 
8069         Throws:
8070             $(REF DateTimeException,std,datetime,date) if the given hour would
8071             result in an invalid $(LREF TimeOfDay).
8072      +/
hourTimeOfDay8073     @property void hour(int hour) @safe pure
8074     {
8075         enforceValid!"hours"(hour);
8076         _hour = cast(ubyte) hour;
8077     }
8078 
8079     @safe unittest
8080     {
8081         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
8082 
8083         auto tod = TimeOfDay(0, 0, 0);
8084         tod.hour = 12;
8085         assert(tod == TimeOfDay(12, 0, 0));
8086 
8087         const ctod = TimeOfDay(0, 0, 0);
8088         immutable itod = TimeOfDay(0, 0, 0);
8089         static assert(!__traits(compiles, ctod.hour = 12));
8090         static assert(!__traits(compiles, itod.hour = 12));
8091     }
8092 
8093 
8094     /++
8095         Minutes past the hour.
8096      +/
8097     @property ubyte minute() const @safe pure nothrow @nogc
8098     {
8099         return _minute;
8100     }
8101 
8102     @safe unittest
8103     {
8104         assert(TimeOfDay.init.minute == 0);
8105         assert(TimeOfDay(0, 30, 0).minute == 30);
8106 
8107         const ctod = TimeOfDay(0, 30, 0);
8108         immutable itod = TimeOfDay(0, 30, 0);
8109         assert(ctod.minute == 30);
8110         assert(itod.minute == 30);
8111     }
8112 
8113 
8114     /++
8115         Minutes past the hour.
8116 
8117         Params:
8118             minute = The minute to set this $(LREF TimeOfDay)'s minute to.
8119 
8120         Throws:
8121             $(REF DateTimeException,std,datetime,date) if the given minute
8122             would result in an invalid $(LREF TimeOfDay).
8123      +/
8124     @property void minute(int minute) @safe pure
8125     {
8126         enforceValid!"minutes"(minute);
8127         _minute = cast(ubyte) minute;
8128     }
8129 
8130     @safe unittest
8131     {
8132         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
8133 
8134         auto tod = TimeOfDay(0, 0, 0);
8135         tod.minute = 30;
8136         assert(tod == TimeOfDay(0, 30, 0));
8137 
8138         const ctod = TimeOfDay(0, 0, 0);
8139         immutable itod = TimeOfDay(0, 0, 0);
8140         static assert(!__traits(compiles, ctod.minute = 30));
8141         static assert(!__traits(compiles, itod.minute = 30));
8142     }
8143 
8144 
8145     /++
8146         Seconds past the minute.
8147      +/
secondTimeOfDay8148     @property ubyte second() const @safe pure nothrow @nogc
8149     {
8150         return _second;
8151     }
8152 
8153     @safe unittest
8154     {
8155         assert(TimeOfDay.init.second == 0);
8156         assert(TimeOfDay(0, 0, 33).second == 33);
8157 
8158         const ctod = TimeOfDay(0, 0, 33);
8159         immutable itod = TimeOfDay(0, 0, 33);
8160         assert(ctod.second == 33);
8161         assert(itod.second == 33);
8162     }
8163 
8164 
8165     /++
8166         Seconds past the minute.
8167 
8168         Params:
8169             second = The second to set this $(LREF TimeOfDay)'s second to.
8170 
8171         Throws:
8172             $(REF DateTimeException,std,datetime,date) if the given second
8173             would result in an invalid $(LREF TimeOfDay).
8174      +/
secondTimeOfDay8175     @property void second(int second) @safe pure
8176     {
8177         enforceValid!"seconds"(second);
8178         _second = cast(ubyte) second;
8179     }
8180 
8181     @safe unittest
8182     {
8183         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
8184 
8185         auto tod = TimeOfDay(0, 0, 0);
8186         tod.second = 33;
8187         assert(tod == TimeOfDay(0, 0, 33));
8188 
8189         const ctod = TimeOfDay(0, 0, 0);
8190         immutable itod = TimeOfDay(0, 0, 0);
8191         static assert(!__traits(compiles, ctod.second = 33));
8192         static assert(!__traits(compiles, itod.second = 33));
8193     }
8194 
8195 
8196     /++
8197         Adds the given number of units to this $(LREF TimeOfDay). A negative
8198         number will subtract.
8199 
8200         The difference between rolling and adding is that rolling does not
8201         affect larger units. For instance, rolling a $(LREF TimeOfDay)
8202         one hours's worth of minutes gets the exact same
8203         $(LREF TimeOfDay).
8204 
8205         Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds").
8206 
8207         Params:
8208             units = The units to add.
8209             value = The number of $(D_PARAM units) to add to this
8210                     $(LREF TimeOfDay).
8211       +/
8212     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8213         if (units == "hours")
8214     {
8215         return this += dur!"hours"(value);
8216     }
8217 
8218     ///
8219     @safe unittest
8220     {
8221         auto tod1 = TimeOfDay(7, 12, 0);
8222         tod1.roll!"hours"(1);
8223         assert(tod1 == TimeOfDay(8, 12, 0));
8224 
8225         auto tod2 = TimeOfDay(7, 12, 0);
8226         tod2.roll!"hours"(-1);
8227         assert(tod2 == TimeOfDay(6, 12, 0));
8228 
8229         auto tod3 = TimeOfDay(23, 59, 0);
8230         tod3.roll!"minutes"(1);
8231         assert(tod3 == TimeOfDay(23, 0, 0));
8232 
8233         auto tod4 = TimeOfDay(0, 0, 0);
8234         tod4.roll!"minutes"(-1);
8235         assert(tod4 == TimeOfDay(0, 59, 0));
8236 
8237         auto tod5 = TimeOfDay(23, 59, 59);
8238         tod5.roll!"seconds"(1);
8239         assert(tod5 == TimeOfDay(23, 59, 0));
8240 
8241         auto tod6 = TimeOfDay(0, 0, 0);
8242         tod6.roll!"seconds"(-1);
8243         assert(tod6 == TimeOfDay(0, 0, 59));
8244     }
8245 
8246     @safe unittest
8247     {
8248         auto tod = TimeOfDay(12, 27, 2);
8249         tod.roll!"hours"(22).roll!"hours"(-7);
8250         assert(tod == TimeOfDay(3, 27, 2));
8251 
8252         const ctod = TimeOfDay(0, 0, 0);
8253         immutable itod = TimeOfDay(0, 0, 0);
8254         static assert(!__traits(compiles, ctod.roll!"hours"(53)));
8255         static assert(!__traits(compiles, itod.roll!"hours"(53)));
8256     }
8257 
8258 
8259     // Shares documentation with "hours" version.
8260     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8261         if (units == "minutes" || units == "seconds")
8262     {
8263         import std.format : format;
8264 
8265         enum memberVarStr = units[0 .. $ - 1];
8266         value %= 60;
8267         mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
8268 
8269         if (value < 0)
8270         {
8271             if (newVal < 0)
8272                 newVal += 60;
8273         }
8274         else if (newVal >= 60)
8275             newVal -= 60;
8276 
8277         mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
8278         return this;
8279     }
8280 
8281     // Test roll!"minutes"().
8282     @safe unittest
8283     {
8284         static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__)
8285         {
8286             orig.roll!"minutes"(minutes);
8287             assert(orig == expected);
8288         }
8289 
8290         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8291         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
8292         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
8293         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
8294         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
8295         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
8296         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
8297         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
8298         testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
8299         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
8300         testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
8301         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8302         testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
8303         testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
8304         testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
8305 
8306         testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
8307         testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
8308         testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
8309         testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
8310         testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
8311         testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
8312         testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
8313         testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
8314 
8315         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
8316         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
8317         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
8318         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
8319         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
8320         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
8321         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
8322         testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
8323         testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
8324         testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
8325         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8326         testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
8327         testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
8328         testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
8329 
8330         testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
8331         testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
8332         testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
8333         testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
8334         testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
8335         testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
8336         testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
8337         testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
8338 
8339         testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
8340         testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
8341         testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
8342 
8343         testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
8344         testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
8345         testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
8346 
8347         testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
8348         testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
8349         testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
8350 
8351         testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
8352         testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
8353         testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
8354 
8355         auto tod = TimeOfDay(12, 27, 2);
8356         tod.roll!"minutes"(97).roll!"minutes"(-102);
8357         assert(tod == TimeOfDay(12, 22, 2));
8358 
8359         const ctod = TimeOfDay(0, 0, 0);
8360         immutable itod = TimeOfDay(0, 0, 0);
8361         static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
8362         static assert(!__traits(compiles, itod.roll!"minutes"(7)));
8363     }
8364 
8365     // Test roll!"seconds"().
8366     @safe unittest
8367     {
8368         static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__)
8369         {
8370             orig.roll!"seconds"(seconds);
8371             assert(orig == expected);
8372         }
8373 
8374         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8375         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
8376         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
8377         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
8378         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
8379         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
8380         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
8381         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
8382         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
8383         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
8384         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
8385         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
8386         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8387         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
8388 
8389         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
8390         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
8391         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
8392         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
8393         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
8394         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
8395         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
8396         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
8397 
8398         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
8399         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
8400         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
8401         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
8402         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
8403         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
8404         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
8405         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
8406         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
8407         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
8408         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
8409         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8410         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
8411 
8412         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
8413         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
8414         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
8415 
8416         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
8417         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
8418         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
8419 
8420         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
8421         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
8422         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
8423 
8424         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
8425         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
8426         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
8427 
8428         auto tod = TimeOfDay(12, 27, 2);
8429         tod.roll!"seconds"(105).roll!"seconds"(-77);
8430         assert(tod == TimeOfDay(12, 27, 30));
8431 
8432         const ctod = TimeOfDay(0, 0, 0);
8433         immutable itod = TimeOfDay(0, 0, 0);
8434         static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
8435         static assert(!__traits(compiles, itod.roll!"seconds"(7)));
8436     }
8437 
8438 
8439     /++
8440         Gives the result of adding or subtracting a $(REF Duration, core,time)
8441         from this $(LREF TimeOfDay).
8442 
8443         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8444         are
8445 
8446         $(BOOKTABLE,
8447         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8448         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8449         )
8450 
8451         Params:
8452             duration = The $(REF Duration, core,time) to add to or subtract from
8453                        this $(LREF TimeOfDay).
8454       +/
8455     TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
8456         if (op == "+" || op == "-")
8457     {
8458         TimeOfDay retval = this;
8459         immutable seconds = duration.total!"seconds";
8460         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
8461     }
8462 
8463     ///
8464     @safe unittest
8465     {
8466         import core.time : hours, minutes, seconds;
8467 
8468         assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
8469         assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
8470         assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
8471         assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
8472 
8473         assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
8474         assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
8475         assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
8476         assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
8477     }
8478 
8479     @safe unittest
8480     {
8481         auto tod = TimeOfDay(12, 30, 33);
8482 
8483         assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8484         assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8485         assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8486         assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8487         assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8488         assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8489 
8490         assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8491         assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8492         assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8493         assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8494         assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8495         assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8496 
8497         assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8498         assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8499         assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8500         assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8501         assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8502         assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8503 
8504         assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8505         assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8506         assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8507         assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8508         assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8509         assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8510 
8511         auto duration = dur!"hours"(11);
8512         const ctod = TimeOfDay(12, 30, 33);
8513         immutable itod = TimeOfDay(12, 30, 33);
8514         assert(tod + duration == TimeOfDay(23, 30, 33));
8515         assert(ctod + duration == TimeOfDay(23, 30, 33));
8516         assert(itod + duration == TimeOfDay(23, 30, 33));
8517 
8518         assert(tod - duration == TimeOfDay(1, 30, 33));
8519         assert(ctod - duration == TimeOfDay(1, 30, 33));
8520         assert(itod - duration == TimeOfDay(1, 30, 33));
8521     }
8522 
8523 
8524     /++
8525         Gives the result of adding or subtracting a $(REF Duration, core,time)
8526         from this $(LREF TimeOfDay), as well as assigning the result to this
8527         $(LREF TimeOfDay).
8528 
8529         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8530         are
8531 
8532         $(BOOKTABLE,
8533         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8534         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8535         )
8536 
8537         Params:
8538             duration = The $(REF Duration, core,time) to add to or subtract from
8539                        this $(LREF TimeOfDay).
8540       +/
8541     ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
8542         if (op == "+" || op == "-")
8543     {
8544         immutable seconds = duration.total!"seconds";
8545         mixin("return _addSeconds(" ~ op ~ "seconds);");
8546     }
8547 
8548     @safe unittest
8549     {
8550         auto duration = dur!"hours"(12);
8551 
8552         assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8553         assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8554         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8555         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8556         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8557         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8558 
8559         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8560         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8561         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8562         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8563         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8564         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8565 
8566         assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8567         assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8568         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8569         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8570         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8571         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8572 
8573         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8574         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8575         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8576         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8577         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8578         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8579 
8580         auto tod = TimeOfDay(19, 17, 22);
8581         (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
8582         assert(tod == TimeOfDay(17, 15, 59));
8583 
8584         const ctod = TimeOfDay(12, 33, 30);
8585         immutable itod = TimeOfDay(12, 33, 30);
8586         static assert(!__traits(compiles, ctod += duration));
8587         static assert(!__traits(compiles, itod += duration));
8588         static assert(!__traits(compiles, ctod -= duration));
8589         static assert(!__traits(compiles, itod -= duration));
8590     }
8591 
8592 
8593     /++
8594         Gives the difference between two $(LREF TimeOfDay)s.
8595 
8596         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8597         are
8598 
8599         $(BOOKTABLE,
8600         $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
8601         )
8602 
8603         Params:
8604             rhs = The $(LREF TimeOfDay) to subtract from this one.
8605       +/
8606     Duration opBinary(string op)(in TimeOfDay rhs) const @safe pure nothrow @nogc
8607         if (op == "-")
8608     {
8609         immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
8610         immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
8611 
8612         return dur!"seconds"(lhsSec - rhsSec);
8613     }
8614 
8615     @safe unittest
8616     {
8617         auto tod = TimeOfDay(12, 30, 33);
8618 
8619         assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
8620         assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
8621         assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
8622         assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
8623         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
8624         assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
8625         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
8626         assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
8627 
8628         const ctod = TimeOfDay(12, 30, 33);
8629         immutable itod = TimeOfDay(12, 30, 33);
8630         assert(tod - tod == Duration.zero);
8631         assert(ctod - tod == Duration.zero);
8632         assert(itod - tod == Duration.zero);
8633 
8634         assert(tod - ctod == Duration.zero);
8635         assert(ctod - ctod == Duration.zero);
8636         assert(itod - ctod == Duration.zero);
8637 
8638         assert(tod - itod == Duration.zero);
8639         assert(ctod - itod == Duration.zero);
8640         assert(itod - itod == Duration.zero);
8641     }
8642 
8643 
8644     /++
8645         Converts this $(LREF TimeOfDay) to a string with the format HHMMSS.
8646       +/
toISOStringTimeOfDay8647     string toISOString() const @safe pure nothrow
8648     {
8649         import std.format : format;
8650         try
8651             return format("%02d%02d%02d", _hour, _minute, _second);
8652         catch (Exception e)
8653             assert(0, "format() threw.");
8654     }
8655 
8656     ///
8657     @safe unittest
8658     {
8659         assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
8660         assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
8661     }
8662 
8663     @safe unittest
8664     {
8665         auto tod = TimeOfDay(12, 30, 33);
8666         const ctod = TimeOfDay(12, 30, 33);
8667         immutable itod = TimeOfDay(12, 30, 33);
8668         assert(tod.toISOString() == "123033");
8669         assert(ctod.toISOString() == "123033");
8670         assert(itod.toISOString() == "123033");
8671     }
8672 
8673 
8674     /++
8675         Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS.
8676       +/
toISOExtStringTimeOfDay8677     string toISOExtString() const @safe pure nothrow
8678     {
8679         import std.format : format;
8680         try
8681             return format("%02d:%02d:%02d", _hour, _minute, _second);
8682         catch (Exception e)
8683             assert(0, "format() threw.");
8684     }
8685 
8686     ///
8687     @safe unittest
8688     {
8689         assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
8690         assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
8691     }
8692 
8693     @safe unittest
8694     {
8695         auto tod = TimeOfDay(12, 30, 33);
8696         const ctod = TimeOfDay(12, 30, 33);
8697         immutable itod = TimeOfDay(12, 30, 33);
8698         assert(tod.toISOExtString() == "12:30:33");
8699         assert(ctod.toISOExtString() == "12:30:33");
8700         assert(itod.toISOExtString() == "12:30:33");
8701     }
8702 
8703 
8704     /++
8705         Converts this TimeOfDay to a string.
8706 
8707         This function exists to make it easy to convert a $(LREF TimeOfDay) to a
8708         string for code that does not care what the exact format is - just that
8709         it presents the information in a clear manner. It also makes it easy to
8710         simply convert a $(LREF TimeOfDay) to a string when using functions such
8711         as `to!string`, `format`, or `writeln` which use toString to convert
8712         user-defined types. So, it is unlikely that much code will call
8713         toString directly.
8714 
8715         The format of the string is purposefully unspecified, and code that
8716         cares about the format of the string should use `toISOString`,
8717         `toISOExtString`, or some other custom formatting function that
8718         explicitly generates the format that the code needs. The reason is that
8719         the code is then clear about what format it's using, making it less
8720         error-prone to maintain the code and interact with other software that
8721         consumes the generated strings. It's for this same reason that
8722         $(LREF TimeOfDay) has no `fromString` function, whereas it does have
8723         `fromISOString` and `fromISOExtString`.
8724 
8725         The format returned by toString may or may not change in the future.
8726       +/
toStringTimeOfDay8727     string toString() const @safe pure nothrow
8728     {
8729         return toISOExtString();
8730     }
8731 
8732     @safe unittest
8733     {
8734         auto tod = TimeOfDay(12, 30, 33);
8735         const ctod = TimeOfDay(12, 30, 33);
8736         immutable itod = TimeOfDay(12, 30, 33);
8737         assert(tod.toString());
8738         assert(ctod.toString());
8739         assert(itod.toString());
8740     }
8741 
8742 
8743     /++
8744         Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
8745         Whitespace is stripped from the given string.
8746 
8747         Params:
8748             isoString = A string formatted in the ISO format for times.
8749 
8750         Throws:
8751             $(REF DateTimeException,std,datetime,date) if the given string is
8752             not in the ISO format or if the resulting $(LREF TimeOfDay) would
8753             not be valid.
8754       +/
8755     static TimeOfDay fromISOString(S)(in S isoString) @safe pure
8756         if (isSomeString!S)
8757     {
8758         import std.conv : to, text, ConvException;
8759         import std.exception : enforce;
8760         import std.string : strip;
8761 
8762         int hours, minutes, seconds;
8763         auto str = strip(isoString);
8764 
8765         enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString));
8766 
8767         try
8768         {
8769             // cast to int from uint is used because it checks for
8770             // non digits without extra loops
8771             hours = cast(int) to!uint(str[0 .. 2]);
8772             minutes = cast(int) to!uint(str[2 .. 4]);
8773             seconds = cast(int) to!uint(str[4 .. $]);
8774         }
catchTimeOfDay8775         catch (ConvException)
8776         {
8777             throw new DateTimeException(text("Invalid ISO String: ", isoString));
8778         }
8779 
8780         return TimeOfDay(hours, minutes, seconds);
8781     }
8782 
8783     ///
8784     @safe unittest
8785     {
8786         assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
8787         assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
8788         assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
8789     }
8790 
8791     @safe unittest
8792     {
8793         assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
8794         assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
8795         assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
8796         assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
8797         assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
8798         assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
8799         assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
8800         assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
8801         assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
8802         assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
8803         assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
8804         assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
8805         assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
8806         assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
8807         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
8808         assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
8809         assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
8810         assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
8811         assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
8812         assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
8813         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
8814         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
8815 
8816         assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
8817         assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
8818         assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
8819         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
8820         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
8821         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
8822         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
8823         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
8824         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
8825         assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
8826         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
8827         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
8828         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
8829         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
8830         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
8831         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
8832         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
8833         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
8834         assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
8835         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
8836         assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
8837         assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
8838         assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
8839         assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
8840         assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
8841         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
8842         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
8843 
8844         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
8845 
8846         assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
8847         assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
8848         assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
8849         assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
8850         assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
8851         assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
8852     }
8853 
8854     // bug# 17801
8855     @safe unittest
8856     {
8857         import std.conv : to;
8858         import std.meta : AliasSeq;
8859         foreach (C; AliasSeq!(char, wchar, dchar))
8860         {
8861             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
8862                 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
8863         }
8864     }
8865 
8866 
8867     /++
8868         Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
8869         Whitespace is stripped from the given string.
8870 
8871         Params:
8872             isoExtString = A string formatted in the ISO Extended format for
8873             times.
8874 
8875         Throws:
8876             $(REF DateTimeException,std,datetime,date) if the given string is
8877             not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
8878             would not be valid.
8879       +/
8880     static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure
8881         if (isSomeString!S)
8882     {
8883         import std.algorithm.searching : all;
8884         import std.ascii : isDigit;
8885         import std.conv : to;
8886         import std.exception : enforce;
8887         import std.format : format;
8888         import std.string : strip;
8889 
8890         auto dstr = to!dstring(strip(isoExtString));
8891 
8892         enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
8893 
8894         auto hours = dstr[0 .. 2];
8895         auto minutes = dstr[3 .. 5];
8896         auto seconds = dstr[6 .. $];
8897 
8898         enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
8899         enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
8900         enforce(all!isDigit(hours),
8901                 new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
8902         enforce(all!isDigit(minutes),
8903                 new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
8904         enforce(all!isDigit(seconds),
8905                 new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
8906 
8907         return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds));
8908     }
8909 
8910     ///
8911     @safe unittest
8912     {
8913         assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
8914         assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
8915         assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
8916     }
8917 
8918     @safe unittest
8919     {
8920         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
8921         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
8922         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
8923         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
8924         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
8925         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
8926         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
8927         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
8928         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
8929         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
8930         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
8931         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
8932         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
8933         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
8934         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
8935         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
8936         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
8937         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
8938         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
8939         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
8940         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
8941         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
8942 
8943         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
8944         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
8945         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
8946         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
8947         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
8948         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
8949         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
8950         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
8951         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
8952         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
8953         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
8954         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
8955         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
8956         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
8957         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
8958         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
8959         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
8960         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
8961         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
8962         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
8963         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
8964         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
8965         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
8966         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
8967         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
8968         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
8969         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
8970 
8971         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
8972 
8973         assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
8974         assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
8975         assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
8976         assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
8977         assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
8978         assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
8979     }
8980 
8981     // bug# 17801
8982     @safe unittest
8983     {
8984         import std.conv : to;
8985         import std.meta : AliasSeq;
8986         foreach (C; AliasSeq!(char, wchar, dchar))
8987         {
8988             foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
8989                 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
8990         }
8991     }
8992 
8993 
8994     /++
8995         Returns midnight.
8996       +/
minTimeOfDay8997     @property static TimeOfDay min() @safe pure nothrow @nogc
8998     {
8999         return TimeOfDay.init;
9000     }
9001 
9002     @safe unittest
9003     {
9004         assert(TimeOfDay.min.hour == 0);
9005         assert(TimeOfDay.min.minute == 0);
9006         assert(TimeOfDay.min.second == 0);
9007         assert(TimeOfDay.min < TimeOfDay.max);
9008     }
9009 
9010 
9011     /++
9012         Returns one second short of midnight.
9013       +/
maxTimeOfDay9014     @property static TimeOfDay max() @safe pure nothrow @nogc
9015     {
9016         auto tod = TimeOfDay.init;
9017         tod._hour = maxHour;
9018         tod._minute = maxMinute;
9019         tod._second = maxSecond;
9020 
9021         return tod;
9022     }
9023 
9024     @safe unittest
9025     {
9026         assert(TimeOfDay.max.hour == 23);
9027         assert(TimeOfDay.max.minute == 59);
9028         assert(TimeOfDay.max.second == 59);
9029         assert(TimeOfDay.max > TimeOfDay.min);
9030     }
9031 
9032 
9033 private:
9034 
9035     /+
9036         Add seconds to the time of day. Negative values will subtract. If the
9037         number of seconds overflows (or underflows), then the seconds will wrap,
9038         increasing (or decreasing) the number of minutes accordingly. If the
9039         number of minutes overflows (or underflows), then the minutes will wrap.
9040         If the number of minutes overflows(or underflows), then the hour will
9041         wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
9042 
9043         Params:
9044             seconds = The number of seconds to add to this TimeOfDay.
9045       +/
9046     ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
9047     {
9048         long hnsecs = convert!("seconds", "hnsecs")(seconds);
9049         hnsecs += convert!("hours", "hnsecs")(_hour);
9050         hnsecs += convert!("minutes", "hnsecs")(_minute);
9051         hnsecs += convert!("seconds", "hnsecs")(_second);
9052 
9053         hnsecs %= convert!("days", "hnsecs")(1);
9054 
9055         if (hnsecs < 0)
9056             hnsecs += convert!("days", "hnsecs")(1);
9057 
9058         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
9059         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9060         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
9061 
9062         _hour = cast(ubyte) newHours;
9063         _minute = cast(ubyte) newMinutes;
9064         _second = cast(ubyte) newSeconds;
9065 
9066         return this;
9067     }
9068 
9069     @safe unittest
9070     {
9071         static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__)
9072         {
9073             orig._addSeconds(seconds);
9074             assert(orig == expected);
9075         }
9076 
9077         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
9078         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
9079         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
9080         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
9081         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
9082         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
9083         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
9084         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
9085         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
9086         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
9087         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
9088         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
9089         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
9090         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
9091 
9092         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
9093         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
9094         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
9095         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
9096         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
9097         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
9098         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
9099         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
9100 
9101         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
9102         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
9103         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
9104         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
9105         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
9106         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
9107         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
9108         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
9109         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
9110         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
9111         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
9112         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
9113         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
9114 
9115         testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
9116         testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
9117         testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
9118         testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
9119         testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
9120         testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
9121 
9122         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
9123         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
9124         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
9125 
9126         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
9127         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
9128         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
9129 
9130         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
9131         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
9132         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
9133 
9134         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
9135         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
9136         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
9137 
9138         const ctod = TimeOfDay(0, 0, 0);
9139         immutable itod = TimeOfDay(0, 0, 0);
9140         static assert(!__traits(compiles, ctod._addSeconds(7)));
9141         static assert(!__traits(compiles, itod._addSeconds(7)));
9142     }
9143 
9144 
9145     /+
9146         Whether the given values form a valid $(LREF TimeOfDay).
9147      +/
9148     static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
9149     {
9150         return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
9151     }
9152 
9153 
9154     @safe pure invariant()
9155     {
9156         import std.format : format;
9157         assert(_valid(_hour, _minute, _second),
9158                format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
9159     }
9160 
9161 
9162 package:
9163 
9164     ubyte _hour;
9165     ubyte _minute;
9166     ubyte _second;
9167 
9168     enum ubyte maxHour   = 24 - 1;
9169     enum ubyte maxMinute = 60 - 1;
9170     enum ubyte maxSecond = 60 - 1;
9171 }
9172 
9173 
9174 /++
9175     Returns whether the given value is valid for the given unit type when in a
9176     time point. Naturally, a duration is not held to a particular range, but
9177     the values in a time point are (e.g. a month must be in the range of
9178     1 - 12 inclusive).
9179 
9180     Params:
9181         units = The units of time to validate.
9182         value = The number to validate.
9183   +/
valid(string units)9184 bool valid(string units)(int value) @safe pure nothrow @nogc
9185 if (units == "months" ||
9186     units == "hours" ||
9187     units == "minutes" ||
9188     units == "seconds")
9189 {
9190     static if (units == "months")
9191         return value >= Month.jan && value <= Month.dec;
9192     else static if (units == "hours")
9193         return value >= 0 && value <= 23;
9194     else static if (units == "minutes")
9195         return value >= 0 && value <= 59;
9196     else static if (units == "seconds")
9197         return value >= 0 && value <= 59;
9198 }
9199 
9200 ///
9201 @safe unittest
9202 {
9203     assert(valid!"hours"(12));
9204     assert(!valid!"hours"(32));
9205     assert(valid!"months"(12));
9206     assert(!valid!"months"(13));
9207 }
9208 
9209 /++
9210     Returns whether the given day is valid for the given year and month.
9211 
9212     Params:
9213         units = The units of time to validate.
9214         year  = The year of the day to validate.
9215         month = The month of the day to validate (January is 1).
9216         day   = The day to validate.
9217   +/
9218 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9219 if (units == "days")
9220 {
9221     return day > 0 && day <= maxDay(year, month);
9222 }
9223 
9224 ///
9225 @safe pure nothrow @nogc unittest
9226 {
9227     assert(valid!"days"(2016, 2, 29));
9228     assert(!valid!"days"(2016, 2, 30));
9229     assert(valid!"days"(2017, 2, 20));
9230     assert(!valid!"days"(2017, 2, 29));
9231 }
9232 
9233 
9234 /++
9235     Params:
9236         units = The units of time to validate.
9237         value = The number to validate.
9238         file  = The file that the $(LREF DateTimeException) will list if thrown.
9239         line  = The line number that the $(LREF DateTimeException) will list if
9240                 thrown.
9241 
9242     Throws:
9243         $(LREF DateTimeException) if $(D valid!units(value)) is false.
9244   +/
9245 void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9246 if (units == "months" ||
9247     units == "hours" ||
9248     units == "minutes" ||
9249     units == "seconds")
9250 {
9251     import std.format : format;
9252 
9253     static if (units == "months")
9254     {
9255         if (!valid!units(value))
9256             throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
9257     }
9258     else static if (units == "hours")
9259     {
9260         if (!valid!units(value))
9261             throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
9262     }
9263     else static if (units == "minutes")
9264     {
9265         if (!valid!units(value))
9266             throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
9267     }
9268     else static if (units == "seconds")
9269     {
9270         if (!valid!units(value))
9271             throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
9272     }
9273 }
9274 
9275 
9276 /++
9277     Params:
9278         units = The units of time to validate.
9279         year  = The year of the day to validate.
9280         month = The month of the day to validate.
9281         day   = The day to validate.
9282         file  = The file that the $(LREF DateTimeException) will list if thrown.
9283         line  = The line number that the $(LREF DateTimeException) will list if
9284                 thrown.
9285 
9286     Throws:
9287         $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
9288   +/
9289 void enforceValid(string units)
9290                  (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9291 if (units == "days")
9292 {
9293     import std.format : format;
9294     if (!valid!"days"(year, month, day))
9295         throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
9296 }
9297 
9298 
9299 /++
9300     Returns the number of days from the current day of the week to the given
9301     day of the week. If they are the same, then the result is 0.
9302 
9303     Params:
9304         currDoW = The current day of the week.
9305         dow     = The day of the week to get the number of days to.
9306   +/
daysToDayOfWeek(DayOfWeek currDoW,DayOfWeek dow)9307 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
9308 {
9309     if (currDoW == dow)
9310         return 0;
9311     if (currDoW < dow)
9312         return dow - currDoW;
9313     return DayOfWeek.sat - currDoW + dow + 1;
9314 }
9315 
9316 ///
9317 @safe pure nothrow @nogc unittest
9318 {
9319     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9320     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9321     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9322 }
9323 
9324 @safe unittest
9325 {
9326     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
9327     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
9328     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
9329     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
9330     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
9331     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
9332     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
9333 
9334     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9335     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9336     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
9337     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9338     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
9339     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
9340     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
9341 
9342     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
9343     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
9344     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
9345     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
9346     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
9347     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
9348     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
9349 
9350     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
9351     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
9352     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
9353     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
9354     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
9355     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
9356     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
9357 
9358     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
9359     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
9360     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
9361     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
9362     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
9363     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
9364     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
9365 
9366     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
9367     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
9368     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
9369     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
9370     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
9371     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
9372     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
9373 
9374     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
9375     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
9376     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
9377     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
9378     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
9379     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
9380     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
9381 }
9382 
9383 
9384 /++
9385     Returns the number of months from the current months of the year to the
9386     given month of the year. If they are the same, then the result is 0.
9387 
9388     Params:
9389         currMonth = The current month of the year.
9390         month     = The month of the year to get the number of months to.
9391   +/
monthsToMonth(int currMonth,int month)9392 int monthsToMonth(int currMonth, int month) @safe pure
9393 {
9394     enforceValid!"months"(currMonth);
9395     enforceValid!"months"(month);
9396 
9397     if (currMonth == month)
9398         return 0;
9399     if (currMonth < month)
9400         return month - currMonth;
9401     return Month.dec - currMonth + month;
9402 }
9403 
9404 ///
9405 @safe pure unittest
9406 {
9407     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9408     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9409     assert(monthsToMonth(Month.jul, Month.oct) == 3);
9410 }
9411 
9412 @safe unittest
9413 {
9414     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9415     assert(monthsToMonth(Month.jan, Month.feb) == 1);
9416     assert(monthsToMonth(Month.jan, Month.mar) == 2);
9417     assert(monthsToMonth(Month.jan, Month.apr) == 3);
9418     assert(monthsToMonth(Month.jan, Month.may) == 4);
9419     assert(monthsToMonth(Month.jan, Month.jun) == 5);
9420     assert(monthsToMonth(Month.jan, Month.jul) == 6);
9421     assert(monthsToMonth(Month.jan, Month.aug) == 7);
9422     assert(monthsToMonth(Month.jan, Month.sep) == 8);
9423     assert(monthsToMonth(Month.jan, Month.oct) == 9);
9424     assert(monthsToMonth(Month.jan, Month.nov) == 10);
9425     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9426 
9427     assert(monthsToMonth(Month.may, Month.jan) == 8);
9428     assert(monthsToMonth(Month.may, Month.feb) == 9);
9429     assert(monthsToMonth(Month.may, Month.mar) == 10);
9430     assert(monthsToMonth(Month.may, Month.apr) == 11);
9431     assert(monthsToMonth(Month.may, Month.may) == 0);
9432     assert(monthsToMonth(Month.may, Month.jun) == 1);
9433     assert(monthsToMonth(Month.may, Month.jul) == 2);
9434     assert(monthsToMonth(Month.may, Month.aug) == 3);
9435     assert(monthsToMonth(Month.may, Month.sep) == 4);
9436     assert(monthsToMonth(Month.may, Month.oct) == 5);
9437     assert(monthsToMonth(Month.may, Month.nov) == 6);
9438     assert(monthsToMonth(Month.may, Month.dec) == 7);
9439 
9440     assert(monthsToMonth(Month.oct, Month.jan) == 3);
9441     assert(monthsToMonth(Month.oct, Month.feb) == 4);
9442     assert(monthsToMonth(Month.oct, Month.mar) == 5);
9443     assert(monthsToMonth(Month.oct, Month.apr) == 6);
9444     assert(monthsToMonth(Month.oct, Month.may) == 7);
9445     assert(monthsToMonth(Month.oct, Month.jun) == 8);
9446     assert(monthsToMonth(Month.oct, Month.jul) == 9);
9447     assert(monthsToMonth(Month.oct, Month.aug) == 10);
9448     assert(monthsToMonth(Month.oct, Month.sep) == 11);
9449     assert(monthsToMonth(Month.oct, Month.oct) == 0);
9450     assert(monthsToMonth(Month.oct, Month.nov) == 1);
9451     assert(monthsToMonth(Month.oct, Month.dec) == 2);
9452 
9453     assert(monthsToMonth(Month.dec, Month.jan) == 1);
9454     assert(monthsToMonth(Month.dec, Month.feb) == 2);
9455     assert(monthsToMonth(Month.dec, Month.mar) == 3);
9456     assert(monthsToMonth(Month.dec, Month.apr) == 4);
9457     assert(monthsToMonth(Month.dec, Month.may) == 5);
9458     assert(monthsToMonth(Month.dec, Month.jun) == 6);
9459     assert(monthsToMonth(Month.dec, Month.jul) == 7);
9460     assert(monthsToMonth(Month.dec, Month.aug) == 8);
9461     assert(monthsToMonth(Month.dec, Month.sep) == 9);
9462     assert(monthsToMonth(Month.dec, Month.oct) == 10);
9463     assert(monthsToMonth(Month.dec, Month.nov) == 11);
9464     assert(monthsToMonth(Month.dec, Month.dec) == 0);
9465 }
9466 
9467 
9468 /++
9469     Whether the given Gregorian Year is a leap year.
9470 
9471     Params:
9472         year = The year to to be tested.
9473  +/
yearIsLeapYear(int year)9474 bool yearIsLeapYear(int year) @safe pure nothrow @nogc
9475 {
9476     if (year % 400 == 0)
9477         return true;
9478     if (year % 100 == 0)
9479         return false;
9480     return year % 4 == 0;
9481 }
9482 
9483 ///
9484 @safe unittest
9485 {
foreach(year;[1,2,100,2001,2002,2003,2005,2006,2007,2009,2010])9486     foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
9487     {
9488         assert(!yearIsLeapYear(year));
9489         assert(!yearIsLeapYear(-year));
9490     }
9491 
foreach(year;[0,4,8,400,800,1600,1996,2000,2004,2008,2012])9492     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
9493     {
9494         assert(yearIsLeapYear(year));
9495         assert(yearIsLeapYear(-year));
9496     }
9497 }
9498 
9499 @safe unittest
9500 {
9501     import std.format : format;
foreach(year;[1,2,3,5,6,7,100,200,300,500,600,700,1998,1999,2001,2002,2003,2005,2006,2007,2009,2010,2011])9502     foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
9503                     2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
9504     {
9505         assert(!yearIsLeapYear(year), format("year: %s.", year));
9506         assert(!yearIsLeapYear(-year), format("year: %s.", year));
9507     }
9508 
foreach(year;[0,4,8,400,800,1600,1996,2000,2004,2008,2012])9509     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
9510     {
9511         assert(yearIsLeapYear(year), format("year: %s.", year));
9512         assert(yearIsLeapYear(-year), format("year: %s.", year));
9513     }
9514 }
9515 
9516 
9517 /++
9518     Whether the given type defines all of the necessary functions for it to
9519     function as a time point.
9520 
9521     1. $(D T) must define a static property named $(D min) which is the smallest
9522        value of $(D T) as $(D Unqual!T).
9523 
9524     2. $(D T) must define a static property named $(D max) which is the largest
9525        value of $(D T) as $(D Unqual!T).
9526 
9527     3. $(D T) must define an $(D opBinary) for addition and subtraction that
9528        accepts $(REF Duration, core,time) and returns $(D Unqual!T).
9529 
9530     4. $(D T) must define an $(D opOpAssign) for addition and subtraction that
9531        accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
9532 
9533     5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T)
9534        and returns returns $(REF Duration, core,time).
9535   +/
isTimePoint(T)9536 template isTimePoint(T)
9537 {
9538     import core.time : Duration;
9539     import std.traits : FunctionAttribute, functionAttributes, Unqual;
9540 
9541     enum isTimePoint = hasMin &&
9542                        hasMax &&
9543                        hasOverloadedOpBinaryWithDuration &&
9544                        hasOverloadedOpAssignWithDuration &&
9545                        hasOverloadedOpBinaryWithSelf &&
9546                        !is(U == Duration);
9547 
9548 private:
9549 
9550     alias U = Unqual!T;
9551 
9552     enum hasMin = __traits(hasMember, T, "min") &&
9553                   is(typeof(T.min) == U) &&
9554                   is(typeof({static assert(__traits(isStaticFunction, T.min));}));
9555 
9556     enum hasMax = __traits(hasMember, T, "max") &&
9557                   is(typeof(T.max) == U) &&
9558                   is(typeof({static assert(__traits(isStaticFunction, T.max));}));
9559 
9560     enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
9561                                              is(typeof(T.init - Duration.init) == U);
9562 
9563     enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
9564                                              is(typeof(U.init -= Duration.init) == U) &&
9565                                              is(typeof(
9566                                              {
9567                                                  alias add = U.opOpAssign!"+";
9568                                                  alias sub = U.opOpAssign!"-";
9569                                                  alias FA = FunctionAttribute;
9570                                                  static assert((functionAttributes!add & FA.ref_) != 0);
9571                                                  static assert((functionAttributes!sub & FA.ref_) != 0);
9572                                              }));
9573 
9574     enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
9575 }
9576 
9577 ///
9578 @safe unittest
9579 {
9580     import core.time : Duration;
9581     import std.datetime.interval : Interval;
9582     import std.datetime.systime : SysTime;
9583 
9584     static assert(isTimePoint!Date);
9585     static assert(isTimePoint!DateTime);
9586     static assert(isTimePoint!SysTime);
9587     static assert(isTimePoint!TimeOfDay);
9588 
9589     static assert(!isTimePoint!int);
9590     static assert(!isTimePoint!Duration);
9591     static assert(!isTimePoint!(Interval!SysTime));
9592 }
9593 
9594 @safe unittest
9595 {
9596     import core.time;
9597     import std.datetime.interval;
9598     import std.datetime.systime;
9599     import std.meta : AliasSeq;
9600 
9601     foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
9602     {
9603         static assert(isTimePoint!(const TP), TP.stringof);
9604         static assert(isTimePoint!(immutable TP), TP.stringof);
9605     }
9606 
9607     foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
9608         static assert(!isTimePoint!T, T.stringof);
9609 }
9610 
9611 
9612 /++
9613     Whether all of the given strings are valid units of time.
9614 
9615     $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime
9616     can handle precision greater than hnsecs, and the few functions in core.time
9617     which deal with "nsecs" deal with it explicitly.
9618   +/
validTimeUnits(string[]units...)9619 bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
9620 {
9621     import std.algorithm.searching : canFind;
9622     foreach (str; units)
9623     {
9624         if (!canFind(timeStrings[], str))
9625             return false;
9626     }
9627     return true;
9628 }
9629 
9630 ///
9631 @safe @nogc nothrow unittest
9632 {
9633     assert(validTimeUnits("msecs", "seconds", "minutes"));
9634     assert(validTimeUnits("days", "weeks", "months"));
9635     assert(!validTimeUnits("ms", "seconds", "minutes"));
9636 }
9637 
9638 
9639 /++
9640     Compares two time unit strings. $(D "years") are the largest units and
9641     $(D "hnsecs") are the smallest.
9642 
9643     Returns:
9644         $(BOOKTABLE,
9645         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
9646         $(TR $(TD this == rhs) $(TD 0))
9647         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
9648         )
9649 
9650     Throws:
9651         $(LREF DateTimeException) if either of the given strings is not a valid
9652         time unit string.
9653  +/
cmpTimeUnits(string lhs,string rhs)9654 int cmpTimeUnits(string lhs, string rhs) @safe pure
9655 {
9656     import std.algorithm.searching : countUntil;
9657     import std.exception : enforce;
9658     import std.format : format;
9659 
9660     auto tstrings = timeStrings;
9661     immutable indexOfLHS = countUntil(tstrings, lhs);
9662     immutable indexOfRHS = countUntil(tstrings, rhs);
9663 
9664     enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
9665     enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
9666 
9667     if (indexOfLHS < indexOfRHS)
9668         return -1;
9669     if (indexOfLHS > indexOfRHS)
9670         return 1;
9671 
9672     return 0;
9673 }
9674 
9675 ///
9676 @safe pure unittest
9677 {
9678     assert(cmpTimeUnits("hours", "hours") == 0);
9679     assert(cmpTimeUnits("hours", "weeks") < 0);
9680     assert(cmpTimeUnits("months", "seconds") > 0);
9681 }
9682 
9683 @safe unittest
9684 {
foreach(i,outerUnits;timeStrings)9685     foreach (i, outerUnits; timeStrings)
9686     {
9687         assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
9688 
9689         // For some reason, $ won't compile.
9690         foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
9691             assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
9692     }
9693 
foreach(i,outerUnits;timeStrings)9694     foreach (i, outerUnits; timeStrings)
9695     {
9696         foreach (innerUnits; timeStrings[0 .. i])
9697             assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
9698     }
9699 }
9700 
9701 
9702 /++
9703     Compares two time unit strings at compile time. $(D "years") are the largest
9704     units and $(D "hnsecs") are the smallest.
9705 
9706     This template is used instead of $(D cmpTimeUnits) because exceptions
9707     can't be thrown at compile time and $(D cmpTimeUnits) must enforce that
9708     the strings it's given are valid time unit strings. This template uses a
9709     template constraint instead.
9710 
9711     Returns:
9712         $(BOOKTABLE,
9713         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
9714         $(TR $(TD this == rhs) $(TD 0))
9715         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
9716         )
9717  +/
9718 template CmpTimeUnits(string lhs, string rhs)
9719 if (validTimeUnits(lhs, rhs))
9720 {
9721     enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
9722 }
9723 
9724 
9725 // Helper function for CmpTimeUnits.
cmpTimeUnitsCTFE(string lhs,string rhs)9726 private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
9727 {
9728     import std.algorithm.searching : countUntil;
9729     auto tstrings = timeStrings;
9730     immutable indexOfLHS = countUntil(tstrings, lhs);
9731     immutable indexOfRHS = countUntil(tstrings, rhs);
9732 
9733     if (indexOfLHS < indexOfRHS)
9734         return -1;
9735     if (indexOfLHS > indexOfRHS)
9736         return 1;
9737 
9738     return 0;
9739 }
9740 
9741 @safe unittest
9742 {
9743     import std.format : format;
9744     import std.meta : AliasSeq;
9745 
genTest(size_t index)9746     static string genTest(size_t index)
9747     {
9748         auto currUnits = timeStrings[index];
9749         auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits);
9750 
9751         foreach (units; timeStrings[index + 1 .. $])
9752             test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units);
9753 
9754         foreach (units; timeStrings[0 .. index])
9755             test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units);
9756 
9757         return test;
9758     }
9759 
9760     static assert(timeStrings.length == 10);
9761     foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
9762         mixin(genTest(n));
9763 }
9764 
9765 
9766 package:
9767 
9768 
9769 /+
9770     Array of the short (three letter) names of each month.
9771   +/
9772 immutable string[12] _monthNames = ["Jan",
9773                                     "Feb",
9774                                     "Mar",
9775                                     "Apr",
9776                                     "May",
9777                                     "Jun",
9778                                     "Jul",
9779                                     "Aug",
9780                                     "Sep",
9781                                     "Oct",
9782                                     "Nov",
9783                                     "Dec"];
9784 
9785 /+
9786     The maximum valid Day in the given month in the given year.
9787 
9788     Params:
9789         year  = The year to get the day for.
9790         month = The month of the Gregorian Calendar to get the day for.
9791  +/
maxDay(int year,int month)9792 ubyte maxDay(int year, int month) @safe pure nothrow @nogc
9793 in
9794 {
9795     assert(valid!"months"(month));
9796 }
9797 body
9798 {
9799     switch (month)
9800     {
9801         case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
9802             return 31;
9803         case Month.feb:
9804             return yearIsLeapYear(year) ? 29 : 28;
9805         case Month.apr, Month.jun, Month.sep, Month.nov:
9806             return 30;
9807         default:
9808             assert(0, "Invalid month.");
9809     }
9810 }
9811 
9812 @safe unittest
9813 {
9814     // Test A.D.
9815     assert(maxDay(1999, 1) == 31);
9816     assert(maxDay(1999, 2) == 28);
9817     assert(maxDay(1999, 3) == 31);
9818     assert(maxDay(1999, 4) == 30);
9819     assert(maxDay(1999, 5) == 31);
9820     assert(maxDay(1999, 6) == 30);
9821     assert(maxDay(1999, 7) == 31);
9822     assert(maxDay(1999, 8) == 31);
9823     assert(maxDay(1999, 9) == 30);
9824     assert(maxDay(1999, 10) == 31);
9825     assert(maxDay(1999, 11) == 30);
9826     assert(maxDay(1999, 12) == 31);
9827 
9828     assert(maxDay(2000, 1) == 31);
9829     assert(maxDay(2000, 2) == 29);
9830     assert(maxDay(2000, 3) == 31);
9831     assert(maxDay(2000, 4) == 30);
9832     assert(maxDay(2000, 5) == 31);
9833     assert(maxDay(2000, 6) == 30);
9834     assert(maxDay(2000, 7) == 31);
9835     assert(maxDay(2000, 8) == 31);
9836     assert(maxDay(2000, 9) == 30);
9837     assert(maxDay(2000, 10) == 31);
9838     assert(maxDay(2000, 11) == 30);
9839     assert(maxDay(2000, 12) == 31);
9840 
9841     // Test B.C.
9842     assert(maxDay(-1999, 1) == 31);
9843     assert(maxDay(-1999, 2) == 28);
9844     assert(maxDay(-1999, 3) == 31);
9845     assert(maxDay(-1999, 4) == 30);
9846     assert(maxDay(-1999, 5) == 31);
9847     assert(maxDay(-1999, 6) == 30);
9848     assert(maxDay(-1999, 7) == 31);
9849     assert(maxDay(-1999, 8) == 31);
9850     assert(maxDay(-1999, 9) == 30);
9851     assert(maxDay(-1999, 10) == 31);
9852     assert(maxDay(-1999, 11) == 30);
9853     assert(maxDay(-1999, 12) == 31);
9854 
9855     assert(maxDay(-2000, 1) == 31);
9856     assert(maxDay(-2000, 2) == 29);
9857     assert(maxDay(-2000, 3) == 31);
9858     assert(maxDay(-2000, 4) == 30);
9859     assert(maxDay(-2000, 5) == 31);
9860     assert(maxDay(-2000, 6) == 30);
9861     assert(maxDay(-2000, 7) == 31);
9862     assert(maxDay(-2000, 8) == 31);
9863     assert(maxDay(-2000, 9) == 30);
9864     assert(maxDay(-2000, 10) == 31);
9865     assert(maxDay(-2000, 11) == 30);
9866     assert(maxDay(-2000, 12) == 31);
9867 }
9868 
9869 /+
9870     Splits out a particular unit from hnsecs and gives the value for that
9871     unit and the remaining hnsecs. It really shouldn't be used unless unless
9872     all units larger than the given units have already been split out.
9873 
9874     Params:
9875         units  = The units to split out.
9876         hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
9877                  after splitting out the given units.
9878 
9879     Returns:
9880         The number of the given units from converting hnsecs to those units.
9881   +/
9882 long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
9883 if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
9884 {
9885     import core.time : convert;
9886     immutable value = convert!("hnsecs", units)(hnsecs);
9887     hnsecs -= convert!(units, "hnsecs")(value);
9888     return value;
9889 }
9890 
9891 @safe unittest
9892 {
9893     auto hnsecs = 2595000000007L;
9894     immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
9895     assert(days == 3);
9896     assert(hnsecs == 3000000007);
9897 
9898     immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9899     assert(minutes == 5);
9900     assert(hnsecs == 7);
9901 }
9902 
9903 
9904 /+
9905     Returns the day of the week for the given day of the Gregorian Calendar.
9906 
9907     Params:
9908         day = The day of the Gregorian Calendar for which to get the day of
9909               the week.
9910   +/
getDayOfWeek(int day)9911 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
9912 {
9913     // January 1st, 1 A.D. was a Monday
9914     if (day >= 0)
9915         return cast(DayOfWeek)(day % 7);
9916     else
9917     {
9918         immutable dow = cast(DayOfWeek)((day % 7) + 7);
9919 
9920         if (dow == 7)
9921             return DayOfWeek.sun;
9922         else
9923             return dow;
9924     }
9925 }
9926 
9927 @safe unittest
9928 {
9929     import std.datetime.systime : SysTime;
9930 
9931     // Test A.D.
9932     assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
9933     assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
9934     assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
9935     assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
9936     assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
9937     assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
9938     assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
9939     assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
9940     assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
9941     assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
9942     assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
9943     assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
9944     assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
9945     assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
9946     assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
9947     assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
9948     assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
9949     assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
9950     assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
9951     assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
9952     assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
9953     assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
9954 
9955     // Test B.C.
9956     assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
9957     assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
9958     assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
9959     assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
9960     assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
9961     assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
9962     assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
9963     assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
9964     assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
9965 }
9966 
9967 
9968 private:
9969 
9970 enum daysInYear     = 365;  // The number of days in a non-leap year.
9971 enum daysInLeapYear = 366;  // The numbef or days in a leap year.
9972 enum daysIn4Years   = daysInYear * 3 + daysInLeapYear;  // Number of days in 4 years.
9973 enum daysIn100Years = daysIn4Years * 25 - 1;  // The number of days in 100 years.
9974 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
9975 
9976 /+
9977     Array of integers representing the last days of each month in a year.
9978   +/
9979 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
9980 
9981 /+
9982     Array of integers representing the last days of each month in a leap year.
9983   +/
9984 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
9985 
9986 
9987 /+
9988     Returns the string representation of the given month.
9989   +/
9990 string monthToString(Month month) @safe pure
9991 {
9992     import std.format : format;
9993     assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
9994     return _monthNames[month - Month.jan];
9995 }
9996 
9997 @safe unittest
9998 {
9999     assert(monthToString(Month.jan) == "Jan");
10000     assert(monthToString(Month.feb) == "Feb");
10001     assert(monthToString(Month.mar) == "Mar");
10002     assert(monthToString(Month.apr) == "Apr");
10003     assert(monthToString(Month.may) == "May");
10004     assert(monthToString(Month.jun) == "Jun");
10005     assert(monthToString(Month.jul) == "Jul");
10006     assert(monthToString(Month.aug) == "Aug");
10007     assert(monthToString(Month.sep) == "Sep");
10008     assert(monthToString(Month.oct) == "Oct");
10009     assert(monthToString(Month.nov) == "Nov");
10010     assert(monthToString(Month.dec) == "Dec");
10011 }
10012 
10013 
10014 /+
10015     Returns the Month corresponding to the given string.
10016 
10017     Params:
10018         monthStr = The string representation of the month to get the Month for.
10019 
10020     Throws:
10021         $(REF DateTimeException,std,datetime,date) if the given month is not a
10022         valid month string.
10023   +/
10024 Month monthFromString(string monthStr) @safe pure
10025 {
10026     import std.format : format;
10027     switch (monthStr)
10028     {
10029         case "Jan":
10030             return Month.jan;
10031         case "Feb":
10032             return Month.feb;
10033         case "Mar":
10034             return Month.mar;
10035         case "Apr":
10036             return Month.apr;
10037         case "May":
10038             return Month.may;
10039         case "Jun":
10040             return Month.jun;
10041         case "Jul":
10042             return Month.jul;
10043         case "Aug":
10044             return Month.aug;
10045         case "Sep":
10046             return Month.sep;
10047         case "Oct":
10048             return Month.oct;
10049         case "Nov":
10050             return Month.nov;
10051         case "Dec":
10052             return Month.dec;
10053         default:
10054             throw new DateTimeException(format("Invalid month %s", monthStr));
10055     }
10056 }
10057 
10058 @safe unittest
10059 {
10060     import std.stdio : writeln;
10061     import std.traits : EnumMembers;
10062     foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
10063                       "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
10064     {
10065         scope(failure) writeln(badStr);
10066         assertThrown!DateTimeException(monthFromString(badStr));
10067     }
10068 
10069     foreach (month; EnumMembers!Month)
10070     {
10071         scope(failure) writeln(month);
10072         assert(monthFromString(monthToString(month)) == month);
10073     }
10074 }
10075 
10076 
10077 version (unittest)
10078 {
10079     // All of these helper arrays are sorted in ascending order.
10080     auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
10081     auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
10082 
10083     // I'd use a Tuple, but I get forward reference errors if I try.
10084     struct MonthDay
10085     {
10086         Month month;
10087         short day;
10088 
10089         this(int m, short d)
10090         {
10091             month = cast(Month) m;
10092             day = d;
10093         }
10094     }
10095 
10096     MonthDay[] testMonthDays = [MonthDay(1, 1),
10097                                 MonthDay(1, 2),
10098                                 MonthDay(3, 17),
10099                                 MonthDay(7, 4),
10100                                 MonthDay(10, 27),
10101                                 MonthDay(12, 30),
10102                                 MonthDay(12, 31)];
10103 
10104     auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
10105 
10106     auto testTODs = [TimeOfDay(0, 0, 0),
10107                      TimeOfDay(0, 0, 1),
10108                      TimeOfDay(0, 1, 0),
10109                      TimeOfDay(1, 0, 0),
10110                      TimeOfDay(13, 13, 13),
10111                      TimeOfDay(23, 59, 59)];
10112 
10113     auto testHours = [0, 1, 12, 22, 23];
10114     auto testMinSecs = [0, 1, 30, 58, 59];
10115 
10116     // Throwing exceptions is incredibly expensive, so we want to use a smaller
10117     // set of values for tests using assertThrown.
10118     auto testTODsThrown = [TimeOfDay(0, 0, 0),
10119                            TimeOfDay(13, 13, 13),
10120                            TimeOfDay(23, 59, 59)];
10121 
10122     Date[] testDatesBC;
10123     Date[] testDatesAD;
10124 
10125     DateTime[] testDateTimesBC;
10126     DateTime[] testDateTimesAD;
10127 
10128     // I'd use a Tuple, but I get forward reference errors if I try.
10129     struct GregDay { int day; Date date; }
10130     auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
10131                            GregDay(-735_233, Date(-2012, 1, 1)),
10132                            GregDay(-735_202, Date(-2012, 2, 1)),
10133                            GregDay(-735_175, Date(-2012, 2, 28)),
10134                            GregDay(-735_174, Date(-2012, 2, 29)),
10135                            GregDay(-735_173, Date(-2012, 3, 1)),
10136                            GregDay(-734_502, Date(-2010, 1, 1)),
10137                            GregDay(-734_472, Date(-2010, 1, 31)),
10138                            GregDay(-734_471, Date(-2010, 2, 1)),
10139                            GregDay(-734_444, Date(-2010, 2, 28)),
10140                            GregDay(-734_443, Date(-2010, 3, 1)),
10141                            GregDay(-734_413, Date(-2010, 3, 31)),
10142                            GregDay(-734_412, Date(-2010, 4, 1)),
10143                            GregDay(-734_383, Date(-2010, 4, 30)),
10144                            GregDay(-734_382, Date(-2010, 5, 1)),
10145                            GregDay(-734_352, Date(-2010, 5, 31)),
10146                            GregDay(-734_351, Date(-2010, 6, 1)),
10147                            GregDay(-734_322, Date(-2010, 6, 30)),
10148                            GregDay(-734_321, Date(-2010, 7, 1)),
10149                            GregDay(-734_291, Date(-2010, 7, 31)),
10150                            GregDay(-734_290, Date(-2010, 8, 1)),
10151                            GregDay(-734_260, Date(-2010, 8, 31)),
10152                            GregDay(-734_259, Date(-2010, 9, 1)),
10153                            GregDay(-734_230, Date(-2010, 9, 30)),
10154                            GregDay(-734_229, Date(-2010, 10, 1)),
10155                            GregDay(-734_199, Date(-2010, 10, 31)),
10156                            GregDay(-734_198, Date(-2010, 11, 1)),
10157                            GregDay(-734_169, Date(-2010, 11, 30)),
10158                            GregDay(-734_168, Date(-2010, 12, 1)),
10159                            GregDay(-734_139, Date(-2010, 12, 30)),
10160                            GregDay(-734_138, Date(-2010, 12, 31)),
10161                            GregDay(-731_215, Date(-2001, 1, 1)),
10162                            GregDay(-730_850, Date(-2000, 1, 1)),
10163                            GregDay(-730_849, Date(-2000, 1, 2)),
10164                            GregDay(-730_486, Date(-2000, 12, 30)),
10165                            GregDay(-730_485, Date(-2000, 12, 31)),
10166                            GregDay(-730_484, Date(-1999, 1, 1)),
10167                            GregDay(-694_690, Date(-1901, 1, 1)),
10168                            GregDay(-694_325, Date(-1900, 1, 1)),
10169                            GregDay(-585_118, Date(-1601, 1, 1)),
10170                            GregDay(-584_753, Date(-1600, 1, 1)),
10171                            GregDay(-584_388, Date(-1600, 12, 31)),
10172                            GregDay(-584_387, Date(-1599, 1, 1)),
10173                            GregDay(-365_972, Date(-1001, 1, 1)),
10174                            GregDay(-365_607, Date(-1000, 1, 1)),
10175                            GregDay(-183_351, Date(-501, 1, 1)),
10176                            GregDay(-182_986, Date(-500, 1, 1)),
10177                            GregDay(-182_621, Date(-499, 1, 1)),
10178                            GregDay(-146_827, Date(-401, 1, 1)),
10179                            GregDay(-146_462, Date(-400, 1, 1)),
10180                            GregDay(-146_097, Date(-400, 12, 31)),
10181                            GregDay(-110_302, Date(-301, 1, 1)),
10182                            GregDay(-109_937, Date(-300, 1, 1)),
10183                            GregDay(-73_778, Date(-201, 1, 1)),
10184                            GregDay(-73_413, Date(-200, 1, 1)),
10185                            GregDay(-38_715, Date(-105, 1, 1)),
10186                            GregDay(-37_254, Date(-101, 1, 1)),
10187                            GregDay(-36_889, Date(-100, 1, 1)),
10188                            GregDay(-36_524, Date(-99, 1, 1)),
10189                            GregDay(-36_160, Date(-99, 12, 31)),
10190                            GregDay(-35_794, Date(-97, 1, 1)),
10191                            GregDay(-18_627, Date(-50, 1, 1)),
10192                            GregDay(-18_262, Date(-49, 1, 1)),
10193                            GregDay(-3652, Date(-9, 1, 1)),
10194                            GregDay(-2191, Date(-5, 1, 1)),
10195                            GregDay(-1827, Date(-5, 12, 31)),
10196                            GregDay(-1826, Date(-4, 1, 1)),
10197                            GregDay(-1825, Date(-4, 1, 2)),
10198                            GregDay(-1462, Date(-4, 12, 30)),
10199                            GregDay(-1461, Date(-4, 12, 31)),
10200                            GregDay(-1460, Date(-3, 1, 1)),
10201                            GregDay(-1096, Date(-3, 12, 31)),
10202                            GregDay(-1095, Date(-2, 1, 1)),
10203                            GregDay(-731, Date(-2, 12, 31)),
10204                            GregDay(-730, Date(-1, 1, 1)),
10205                            GregDay(-367, Date(-1, 12, 30)),
10206                            GregDay(-366, Date(-1, 12, 31)),
10207                            GregDay(-365, Date(0, 1, 1)),
10208                            GregDay(-31, Date(0, 11, 30)),
10209                            GregDay(-30, Date(0, 12, 1)),
10210                            GregDay(-1, Date(0, 12, 30)),
10211                            GregDay(0, Date(0, 12, 31))];
10212 
10213     auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)),
10214                            GregDay(2, Date(1, 1, 2)),
10215                            GregDay(32, Date(1, 2, 1)),
10216                            GregDay(365, Date(1, 12, 31)),
10217                            GregDay(366, Date(2, 1, 1)),
10218                            GregDay(731, Date(3, 1, 1)),
10219                            GregDay(1096, Date(4, 1, 1)),
10220                            GregDay(1097, Date(4, 1, 2)),
10221                            GregDay(1460, Date(4, 12, 30)),
10222                            GregDay(1461, Date(4, 12, 31)),
10223                            GregDay(1462, Date(5, 1, 1)),
10224                            GregDay(17_898, Date(50, 1, 1)),
10225                            GregDay(35_065, Date(97, 1, 1)),
10226                            GregDay(36_160, Date(100, 1, 1)),
10227                            GregDay(36_525, Date(101, 1, 1)),
10228                            GregDay(37_986, Date(105, 1, 1)),
10229                            GregDay(72_684, Date(200, 1, 1)),
10230                            GregDay(73_049, Date(201, 1, 1)),
10231                            GregDay(109_208, Date(300, 1, 1)),
10232                            GregDay(109_573, Date(301, 1, 1)),
10233                            GregDay(145_732, Date(400, 1, 1)),
10234                            GregDay(146_098, Date(401, 1, 1)),
10235                            GregDay(182_257, Date(500, 1, 1)),
10236                            GregDay(182_622, Date(501, 1, 1)),
10237                            GregDay(364_878, Date(1000, 1, 1)),
10238                            GregDay(365_243, Date(1001, 1, 1)),
10239                            GregDay(584_023, Date(1600, 1, 1)),
10240                            GregDay(584_389, Date(1601, 1, 1)),
10241                            GregDay(693_596, Date(1900, 1, 1)),
10242                            GregDay(693_961, Date(1901, 1, 1)),
10243                            GregDay(729_755, Date(1999, 1, 1)),
10244                            GregDay(730_120, Date(2000, 1, 1)),
10245                            GregDay(730_121, Date(2000, 1, 2)),
10246                            GregDay(730_484, Date(2000, 12, 30)),
10247                            GregDay(730_485, Date(2000, 12, 31)),
10248                            GregDay(730_486, Date(2001, 1, 1)),
10249                            GregDay(733_773, Date(2010, 1, 1)),
10250                            GregDay(733_774, Date(2010, 1, 2)),
10251                            GregDay(733_803, Date(2010, 1, 31)),
10252                            GregDay(733_804, Date(2010, 2, 1)),
10253                            GregDay(733_831, Date(2010, 2, 28)),
10254                            GregDay(733_832, Date(2010, 3, 1)),
10255                            GregDay(733_862, Date(2010, 3, 31)),
10256                            GregDay(733_863, Date(2010, 4, 1)),
10257                            GregDay(733_892, Date(2010, 4, 30)),
10258                            GregDay(733_893, Date(2010, 5, 1)),
10259                            GregDay(733_923, Date(2010, 5, 31)),
10260                            GregDay(733_924, Date(2010, 6, 1)),
10261                            GregDay(733_953, Date(2010, 6, 30)),
10262                            GregDay(733_954, Date(2010, 7, 1)),
10263                            GregDay(733_984, Date(2010, 7, 31)),
10264                            GregDay(733_985, Date(2010, 8, 1)),
10265                            GregDay(734_015, Date(2010, 8, 31)),
10266                            GregDay(734_016, Date(2010, 9, 1)),
10267                            GregDay(734_045, Date(2010, 9, 30)),
10268                            GregDay(734_046, Date(2010, 10, 1)),
10269                            GregDay(734_076, Date(2010, 10, 31)),
10270                            GregDay(734_077, Date(2010, 11, 1)),
10271                            GregDay(734_106, Date(2010, 11, 30)),
10272                            GregDay(734_107, Date(2010, 12, 1)),
10273                            GregDay(734_136, Date(2010, 12, 30)),
10274                            GregDay(734_137, Date(2010, 12, 31)),
10275                            GregDay(734_503, Date(2012, 1, 1)),
10276                            GregDay(734_534, Date(2012, 2, 1)),
10277                            GregDay(734_561, Date(2012, 2, 28)),
10278                            GregDay(734_562, Date(2012, 2, 29)),
10279                            GregDay(734_563, Date(2012, 3, 1)),
10280                            GregDay(734_858, Date(2012, 12, 21))];
10281 
10282     // I'd use a Tuple, but I get forward reference errors if I try.
10283     struct DayOfYear { int day; MonthDay md; }
10284     auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)),
10285                            DayOfYear(2, MonthDay(1, 2)),
10286                            DayOfYear(3, MonthDay(1, 3)),
10287                            DayOfYear(31, MonthDay(1, 31)),
10288                            DayOfYear(32, MonthDay(2, 1)),
10289                            DayOfYear(59, MonthDay(2, 28)),
10290                            DayOfYear(60, MonthDay(3, 1)),
10291                            DayOfYear(90, MonthDay(3, 31)),
10292                            DayOfYear(91, MonthDay(4, 1)),
10293                            DayOfYear(120, MonthDay(4, 30)),
10294                            DayOfYear(121, MonthDay(5, 1)),
10295                            DayOfYear(151, MonthDay(5, 31)),
10296                            DayOfYear(152, MonthDay(6, 1)),
10297                            DayOfYear(181, MonthDay(6, 30)),
10298                            DayOfYear(182, MonthDay(7, 1)),
10299                            DayOfYear(212, MonthDay(7, 31)),
10300                            DayOfYear(213, MonthDay(8, 1)),
10301                            DayOfYear(243, MonthDay(8, 31)),
10302                            DayOfYear(244, MonthDay(9, 1)),
10303                            DayOfYear(273, MonthDay(9, 30)),
10304                            DayOfYear(274, MonthDay(10, 1)),
10305                            DayOfYear(304, MonthDay(10, 31)),
10306                            DayOfYear(305, MonthDay(11, 1)),
10307                            DayOfYear(334, MonthDay(11, 30)),
10308                            DayOfYear(335, MonthDay(12, 1)),
10309                            DayOfYear(363, MonthDay(12, 29)),
10310                            DayOfYear(364, MonthDay(12, 30)),
10311                            DayOfYear(365, MonthDay(12, 31))];
10312 
10313     auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)),
10314                                DayOfYear(2, MonthDay(1, 2)),
10315                                DayOfYear(3, MonthDay(1, 3)),
10316                                DayOfYear(31, MonthDay(1, 31)),
10317                                DayOfYear(32, MonthDay(2, 1)),
10318                                DayOfYear(59, MonthDay(2, 28)),
10319                                DayOfYear(60, MonthDay(2, 29)),
10320                                DayOfYear(61, MonthDay(3, 1)),
10321                                DayOfYear(91, MonthDay(3, 31)),
10322                                DayOfYear(92, MonthDay(4, 1)),
10323                                DayOfYear(121, MonthDay(4, 30)),
10324                                DayOfYear(122, MonthDay(5, 1)),
10325                                DayOfYear(152, MonthDay(5, 31)),
10326                                DayOfYear(153, MonthDay(6, 1)),
10327                                DayOfYear(182, MonthDay(6, 30)),
10328                                DayOfYear(183, MonthDay(7, 1)),
10329                                DayOfYear(213, MonthDay(7, 31)),
10330                                DayOfYear(214, MonthDay(8, 1)),
10331                                DayOfYear(244, MonthDay(8, 31)),
10332                                DayOfYear(245, MonthDay(9, 1)),
10333                                DayOfYear(274, MonthDay(9, 30)),
10334                                DayOfYear(275, MonthDay(10, 1)),
10335                                DayOfYear(305, MonthDay(10, 31)),
10336                                DayOfYear(306, MonthDay(11, 1)),
10337                                DayOfYear(335, MonthDay(11, 30)),
10338                                DayOfYear(336, MonthDay(12, 1)),
10339                                DayOfYear(364, MonthDay(12, 29)),
10340                                DayOfYear(365, MonthDay(12, 30)),
10341                                DayOfYear(366, MonthDay(12, 31))];
10342 
10343     void initializeTests() @safe
10344     {
10345         foreach (year; testYearsBC)
10346         {
10347             foreach (md; testMonthDays)
10348                 testDatesBC ~= Date(year, md.month, md.day);
10349         }
10350 
10351         foreach (year; testYearsAD)
10352         {
10353             foreach (md; testMonthDays)
10354                 testDatesAD ~= Date(year, md.month, md.day);
10355         }
10356 
10357         foreach (dt; testDatesBC)
10358         {
10359             foreach (tod; testTODs)
10360                 testDateTimesBC ~= DateTime(dt, tod);
10361         }
10362 
10363         foreach (dt; testDatesAD)
10364         {
10365             foreach (tod; testTODs)
10366                 testDateTimesAD ~= DateTime(dt, tod);
10367         }
10368     }
10369 }
10370