1 // Written in the D programming language
2 /++
3 
4 $(SCRIPT inhibitQuickIndex = 1;)
5 $(DIVC quickindex,
6 $(BOOKTABLE,
7 $(TR $(TH Category) $(TH Functions))
8 $(TR $(TD Main date types) $(TD
9     $(LREF Date)
10     $(LREF DateTime)
11 ))
12 $(TR $(TD Other date types) $(TD
13     $(LREF Month)
14     $(LREF DayOfWeek)
15     $(LREF TimeOfDay)
16 ))
17 $(TR $(TD Date checking) $(TD
18     $(LREF valid)
19     $(LREF validTimeUnits)
20     $(LREF yearIsLeapYear)
21     $(LREF isTimePoint)
22     $(LREF enforceValid)
23 ))
24 $(TR $(TD Date conversion) $(TD
25     $(LREF daysToDayOfWeek)
26     $(LREF monthsToMonth)
27 ))
28 $(TR $(TD Time units) $(TD
29     $(LREF cmpTimeUnits)
30     $(LREF timeStrings)
31 ))
32 $(TR $(TD Other) $(TD
33     $(LREF AllowDayOverflow)
34     $(LREF DateTimeException)
35 ))
36 ))
37 
38     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
39     Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis)
40     Source:    $(PHOBOSSRC std/datetime/date.d)
41 +/
42 module std.datetime.date;
43 
44 import core.time : TimeException;
45 import std.traits : isSomeString, Unqual;
46 import std.typecons : Flag;
47 import std.range.primitives : isOutputRange;
48 
49 version (StdUnittest) import std.exception : assertThrown;
50 
51 @safe unittest
52 {
53     initializeTests();
54 }
55 
56 
57 /++
58     Exception type used by std.datetime. It's an alias to
59     $(REF TimeException,core,time). Either can be caught without concern about
60     which module it came from.
61   +/
62 alias DateTimeException = TimeException;
63 
64 
65 /++
66     Represents the 12 months of the Gregorian year (January is 1).
67   +/
68 enum Month : ubyte
69 {
70     jan = 1, ///
71     feb,     ///
72     mar,     ///
73     apr,     ///
74     may,     ///
75     jun,     ///
76     jul,     ///
77     aug,     ///
78     sep,     ///
79     oct,     ///
80     nov,     ///
81     dec      ///
82 }
83 
84 ///
85 @safe pure unittest
86 {
87     assert(Date(2018, 10, 1).month == Month.oct);
88     assert(DateTime(1, 1, 1).month == Month.jan);
89 }
90 
91 
92 /++
93     Represents the 7 days of the Gregorian week (Sunday is 0).
94   +/
95 enum DayOfWeek : ubyte
96 {
97     sun = 0, ///
98     mon,     ///
99     tue,     ///
100     wed,     ///
101     thu,     ///
102     fri,     ///
103     sat      ///
104 }
105 
106 ///
107 @safe pure unittest
108 {
109     assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
110     assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
111 }
112 
113 /++
114     In some date calculations, adding months or years can cause the date to fall
115     on a day of the month which is not valid (e.g. February 29th 2001 or
116     June 31st 2000). If overflow is allowed (as is the default), then the month
117     will be incremented accordingly (so, February 29th 2001 would become
118     March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
119     is not allowed, then the day will be adjusted to the last valid day in that
120     month (so, February 29th 2001 would become February 28th 2001 and
121     June 31st 2000 would become June 30th 2000).
122 
123     AllowDayOverflow only applies to calculations involving months or years.
124 
125     If set to `AllowDayOverflow.no`, then day overflow is not allowed.
126 
127     Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
128     allowed.
129   +/
130 alias AllowDayOverflow = Flag!"allowDayOverflow";
131 
132 
133 /++
134     Array of the strings representing time units, starting with the smallest
135     unit and going to the largest. It does not include `"nsecs"`.
136 
137     Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
138     `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
139     `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
140     `"years"`
141   +/
142 immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
143                                   "hours", "days", "weeks", "months", "years"];
144 
145 
146 /++
147     Combines the $(REF Date,std,datetime,date) and
148     $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
149     both the date and the time. It is optimized for calendar-based operations
150     and has no concept of time zone. For an object which is optimized for time
151     operations based on the system time, use
152     $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
153     a concept of time zone and has much higher precision (hnsecs). `DateTime`
154     is intended primarily for calendar-based uses rather than precise time
155     operations.
156   +/
157 struct DateTime
158 {
159 public:
160 
161     /++
162         Params:
163             date = The date portion of $(LREF DateTime).
164             tod  = The time portion of $(LREF DateTime).
165       +/
166     this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
167     {
168         _date = date;
169         _tod = tod;
170     }
171 
172     @safe unittest
173     {
174         {
175             auto dt = DateTime.init;
176             assert(dt._date == Date.init);
177             assert(dt._tod == TimeOfDay.init);
178         }
179 
180         {
181             auto dt = DateTime(Date(1999, 7 ,6));
182             assert(dt._date == Date(1999, 7, 6));
183             assert(dt._tod == TimeOfDay.init);
184         }
185 
186         {
187             auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33));
188             assert(dt._date == Date(1999, 7, 6));
189             assert(dt._tod == TimeOfDay(12, 30, 33));
190         }
191     }
192 
193 
194     /++
195         Params:
196             year   = The year portion of the date.
197             month  = The month portion of the date (January is 1).
198             day    = The day portion of the date.
199             hour   = The hour portion of the time;
200             minute = The minute portion of the time;
201             second = The second portion of the time;
202       +/
203     this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure
204     {
205         _date = Date(year, month, day);
206         _tod = TimeOfDay(hour, minute, second);
207     }
208 
209     @safe unittest
210     {
211         {
212             auto dt = DateTime(1999, 7 ,6);
213             assert(dt._date == Date(1999, 7, 6));
214             assert(dt._tod == TimeOfDay.init);
215         }
216 
217         {
218             auto dt = DateTime(1999, 7 ,6, 12, 30, 33);
219             assert(dt._date == Date(1999, 7, 6));
220             assert(dt._tod == TimeOfDay(12, 30, 33));
221         }
222     }
223 
224 
225     /++
226         Compares this $(LREF DateTime) with the given `DateTime.`.
227 
228         Returns:
229             $(BOOKTABLE,
230             $(TR $(TD this < rhs) $(TD < 0))
231             $(TR $(TD this == rhs) $(TD 0))
232             $(TR $(TD this > rhs) $(TD > 0))
233             )
234      +/
235     int opCmp(DateTime rhs) const @safe pure nothrow @nogc
236     {
237         immutable dateResult = _date.opCmp(rhs._date);
238 
239         if (dateResult != 0)
240             return dateResult;
241 
242         return _tod.opCmp(rhs._tod);
243     }
244 
245     @safe unittest
246     {
247         // Test A.D.
248         assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0);
249 
250         assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0);
251         assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0);
252         assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0);
253 
254         assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0);
255         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0);
256 
257         assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0);
258 
259         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
260         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
261         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
262         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
263         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0);
264         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
265 
266         assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
267         assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
268         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
269         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
270         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
271         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
272 
273 
274         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp(
275                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0);
276         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp(
277                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0);
278         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp(
279                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0);
280         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
281                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
282 
283         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp(
284                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0);
285         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
286                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
287 
288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp(
289                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0);
290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
291                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
292 
293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
294                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
296                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
298                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
300                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
302                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0);
303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
304                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
305 
306         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
307                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
308         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
309                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
310         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
311                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
312         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
313                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
314 
315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
316                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
317         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
318                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
319 
320         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
321                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
322         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
323                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
324         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
325                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
326         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
327                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
328         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
329                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
330         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
331                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
332 
333         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
334                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
335         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
336                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
337         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
338                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
339         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
340                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
341         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
342                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
343         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
344                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
345 
346         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
347                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
348         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
349                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
350         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
351                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0);
352         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
353                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
354         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
355                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
356         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
357                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
358 
359         // Test B.C.
360         assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp(
361                    DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0);
362         assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
363                    DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0);
364         assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp(
365                    DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0);
366 
367         assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
368                    DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0);
369         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
370                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
371 
372         assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
373                    DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0);
374 
375         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
376                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
377         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
378                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
379         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
380                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
381         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
382                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
383         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
384                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
385         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
386                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
387 
388         assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
389                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
390         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
391                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
392         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
393                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
394         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
395                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
396         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
397                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
398         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
399                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
400 
401         // Test Both
402         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
403                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
404         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
405                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
406 
407         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
408                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
409         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
410                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0);
411 
412         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
413                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
414         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
415                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
416 
417         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
418                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
419         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
420                    DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0);
421 
422         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
423                    DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0);
424         assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp(
425                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
426 
427         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
428         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
429         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
430         assert(dt.opCmp(dt) == 0);
431         assert(dt.opCmp(cdt) == 0);
432         assert(dt.opCmp(idt) == 0);
433         assert(cdt.opCmp(dt) == 0);
434         assert(cdt.opCmp(cdt) == 0);
435         assert(cdt.opCmp(idt) == 0);
436         assert(idt.opCmp(dt) == 0);
437         assert(idt.opCmp(cdt) == 0);
438         assert(idt.opCmp(idt) == 0);
439     }
440 
441 
442     /++
443         The date portion of $(LREF DateTime).
444       +/
445     @property Date date() const @safe pure nothrow @nogc
446     {
447         return _date;
448     }
449 
450     @safe unittest
451     {
452         {
453             auto dt = DateTime.init;
454             assert(dt.date == Date.init);
455         }
456 
457         {
458             auto dt = DateTime(Date(1999, 7, 6));
459             assert(dt.date == Date(1999, 7, 6));
460         }
461 
462         const cdt = DateTime(1999, 7, 6);
463         immutable idt = DateTime(1999, 7, 6);
464         assert(cdt.date == Date(1999, 7, 6));
465         assert(idt.date == Date(1999, 7, 6));
466     }
467 
468 
469     /++
470         The date portion of $(LREF DateTime).
471 
472         Params:
473             date = The Date to set this $(LREF DateTime)'s date portion to.
474       +/
475     @property void date(Date date) @safe pure nothrow @nogc
476     {
477         _date = date;
478     }
479 
480     @safe unittest
481     {
482         auto dt = DateTime.init;
483         dt.date = Date(1999, 7, 6);
484         assert(dt._date == Date(1999, 7, 6));
485         assert(dt._tod == TimeOfDay.init);
486 
487         const cdt = DateTime(1999, 7, 6);
488         immutable idt = DateTime(1999, 7, 6);
489         static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1)));
490         static assert(!__traits(compiles, idt.date = Date(2010, 1, 1)));
491     }
492 
493 
494     /++
495         The time portion of $(LREF DateTime).
496       +/
497     @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
498     {
499         return _tod;
500     }
501 
502     @safe unittest
503     {
504         {
505             auto dt = DateTime.init;
506             assert(dt.timeOfDay == TimeOfDay.init);
507         }
508 
509         {
510             auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33));
511             assert(dt.timeOfDay == TimeOfDay(12, 30, 33));
512         }
513 
514         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
515         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
516         assert(cdt.timeOfDay == TimeOfDay(12, 30, 33));
517         assert(idt.timeOfDay == TimeOfDay(12, 30, 33));
518     }
519 
520 
521     /++
522         The time portion of $(LREF DateTime).
523 
524         Params:
525             tod = The $(REF TimeOfDay,std,datetime,date) to set this
526                   $(LREF DateTime)'s time portion to.
527       +/
528     @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
529     {
530         _tod = tod;
531     }
532 
533     @safe unittest
534     {
535         auto dt = DateTime.init;
536         dt.timeOfDay = TimeOfDay(12, 30, 33);
537         assert(dt._date == Date.init);
538         assert(dt._tod == TimeOfDay(12, 30, 33));
539 
540         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
541         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
542         static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33)));
543         static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33)));
544     }
545 
546 
547     /++
548         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
549         are B.C.
550      +/
551     @property short year() const @safe pure nothrow @nogc
552     {
553         return _date.year;
554     }
555 
556     @safe unittest
557     {
558         assert(Date.init.year == 1);
559         assert(Date(1999, 7, 6).year == 1999);
560         assert(Date(-1999, 7, 6).year == -1999);
561 
562         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
563         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
564         assert(idt.year == 1999);
565         assert(idt.year == 1999);
566     }
567 
568 
569     /++
570         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
571         are B.C.
572 
573         Params:
574             year = The year to set this $(LREF DateTime)'s year to.
575 
576         Throws:
577             $(REF DateTimeException,std,datetime,date) if the new year is not
578             a leap year and if the resulting date would be on February 29th.
579      +/
580     @property void year(int year) @safe pure
581     {
582         _date.year = year;
583     }
584 
585     ///
586     @safe unittest
587     {
588         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
589         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
590         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
591     }
592 
593     @safe unittest
594     {
595         static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
596         {
597             dt.year = year;
598             assert(dt == expected);
599         }
600 
601         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
602                1999,
603                DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)));
604         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
605                0,
606                DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)));
607         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
608                -1999,
609                DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)));
610 
611         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
612         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
613         static assert(!__traits(compiles, cdt.year = 7));
614         static assert(!__traits(compiles, idt.year = 7));
615     }
616 
617 
618     /++
619         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
620 
621         Throws:
622             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
623      +/
624     @property short yearBC() const @safe pure
625     {
626         return _date.yearBC;
627     }
628 
629     ///
630     @safe unittest
631     {
632         assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
633         assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
634         assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
635     }
636 
637     @safe unittest
638     {
639         assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
640 
641         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
642         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
643         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
644         dt.yearBC = 12;
645         assert(dt.yearBC == 12);
646         static assert(!__traits(compiles, cdt.yearBC = 12));
647         static assert(!__traits(compiles, idt.yearBC = 12));
648     }
649 
650 
651     /++
652         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
653 
654         Params:
655             year = The year B.C. to set this $(LREF DateTime)'s year to.
656 
657         Throws:
658             $(REF DateTimeException,std,datetime,date) if a non-positive value
659             is given.
660      +/
661     @property void yearBC(int year) @safe pure
662     {
663         _date.yearBC = year;
664     }
665 
666     ///
667     @safe unittest
668     {
669         auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
670         dt.yearBC = 1;
671         assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
672 
673         dt.yearBC = 10;
674         assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
675     }
676 
677     @safe unittest
678     {
679         assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1))));
680 
681         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
682         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
683         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
684         dt.yearBC = 12;
685         assert(dt.yearBC == 12);
686         static assert(!__traits(compiles, cdt.yearBC = 12));
687         static assert(!__traits(compiles, idt.yearBC = 12));
688     }
689 
690 
691     /++
692         Month of a Gregorian Year.
693      +/
694     @property Month month() const @safe pure nothrow @nogc
695     {
696         return _date.month;
697     }
698 
699     ///
700     @safe unittest
701     {
702         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
703         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
704         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
705     }
706 
707     @safe unittest
708     {
709         assert(DateTime.init.month == 1);
710         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
711         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
712 
713         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
714         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
715         assert(cdt.month == 7);
716         assert(idt.month == 7);
717     }
718 
719 
720     /++
721         Month of a Gregorian Year.
722 
723         Params:
724             month = The month to set this $(LREF DateTime)'s month to.
725 
726         Throws:
727             $(REF DateTimeException,std,datetime,date) if the given month is
728             not a valid month.
729      +/
730     @property void month(Month month) @safe pure
731     {
732         _date.month = month;
733     }
734 
735     @safe unittest
736     {
737         static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
738         {
739             dt.month = month;
740             assert(expected != DateTime.init);
741             assert(dt == expected);
742         }
743 
744         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0));
745         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13));
746 
747         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
748                cast(Month) 7,
749                DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)));
750         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)),
751                cast(Month) 7,
752                DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)));
753 
754         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
755         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
756         static assert(!__traits(compiles, cdt.month = 12));
757         static assert(!__traits(compiles, idt.month = 12));
758     }
759 
760 
761     /++
762         Day of a Gregorian Month.
763      +/
764     @property ubyte day() const @safe pure nothrow @nogc
765     {
766         return _date.day;
767     }
768 
769     ///
770     @safe unittest
771     {
772         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
773         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
774         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
775     }
776 
777     @safe unittest
778     {
779         import std.format : format;
780         import std.range : chain;
781 
782         static void test(DateTime dateTime, int expected)
783         {
784             assert(dateTime.day == expected, format("Value given: %s", dateTime));
785         }
786 
787         foreach (year; chain(testYearsBC, testYearsAD))
788         {
789             foreach (md; testMonthDays)
790             {
791                 foreach (tod; testTODs)
792                     test(DateTime(Date(year, md.month, md.day), tod), md.day);
793             }
794         }
795 
796         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
797         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
798         assert(cdt.day == 6);
799         assert(idt.day == 6);
800     }
801 
802 
803     /++
804         Day of a Gregorian Month.
805 
806         Params:
807             day = The day of the month to set this $(LREF DateTime)'s day to.
808 
809         Throws:
810             $(REF DateTimeException,std,datetime,date) if the given day is not
811             a valid day of the current month.
812      +/
813     @property void day(int day) @safe pure
814     {
815         _date.day = day;
816     }
817 
818     @safe unittest
819     {
820         import std.exception : assertNotThrown;
821 
822         static void testDT(DateTime dt, int day)
823         {
824             dt.day = day;
825         }
826 
827         // Test A.D.
828         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0));
829         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32));
830         assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29));
831         assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30));
832         assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32));
833         assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31));
834         assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32));
835         assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31));
836         assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32));
837         assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32));
838         assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31));
839         assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32));
840         assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31));
841         assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32));
842 
843         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31));
844         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28));
845         assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29));
846         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31));
847         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30));
848         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31));
849         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30));
850         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31));
851         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31));
852         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30));
853         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31));
854         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30));
855         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31));
856 
857         {
858             auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22));
859             dt.day = 6;
860             assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22)));
861         }
862 
863         // Test B.C.
864         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0));
865         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32));
866         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29));
867         assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30));
868         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32));
869         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31));
870         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32));
871         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31));
872         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32));
873         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32));
874         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31));
875         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32));
876         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31));
877         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32));
878 
879         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31));
880         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28));
881         assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29));
882         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31));
883         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30));
884         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31));
885         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30));
886         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31));
887         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31));
888         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30));
889         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31));
890         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30));
891         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31));
892 
893         auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22));
894         dt.day = 6;
895         assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22)));
896 
897         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
898         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
899         static assert(!__traits(compiles, cdt.day = 27));
900         static assert(!__traits(compiles, idt.day = 27));
901     }
902 
903 
904     /++
905         Hours past midnight.
906      +/
907     @property ubyte hour() const @safe pure nothrow @nogc
908     {
909         return _tod.hour;
910     }
911 
912     @safe unittest
913     {
914         assert(DateTime.init.hour == 0);
915         assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12);
916 
917         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
918         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
919         assert(cdt.hour == 12);
920         assert(idt.hour == 12);
921     }
922 
923 
924     /++
925         Hours past midnight.
926 
927         Params:
928             hour = The hour of the day to set this $(LREF DateTime)'s hour to.
929 
930         Throws:
931             $(REF DateTimeException,std,datetime,date) if the given hour would
932             result in an invalid $(LREF DateTime).
933      +/
934     @property void hour(int hour) @safe pure
935     {
936         _tod.hour = hour;
937     }
938 
939     @safe unittest
940     {
941         assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}());
942 
943         auto dt = DateTime.init;
944         dt.hour = 12;
945         assert(dt == DateTime(1, 1, 1, 12, 0, 0));
946 
947         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
948         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
949         static assert(!__traits(compiles, cdt.hour = 27));
950         static assert(!__traits(compiles, idt.hour = 27));
951     }
952 
953 
954     /++
955         Minutes past the hour.
956      +/
957     @property ubyte minute() const @safe pure nothrow @nogc
958     {
959         return _tod.minute;
960     }
961 
962     @safe unittest
963     {
964         assert(DateTime.init.minute == 0);
965         assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30);
966 
967         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
968         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
969         assert(cdt.minute == 30);
970         assert(idt.minute == 30);
971     }
972 
973 
974     /++
975         Minutes past the hour.
976 
977         Params:
978             minute = The minute to set this $(LREF DateTime)'s minute to.
979 
980         Throws:
981             $(REF DateTimeException,std,datetime,date) if the given minute
982             would result in an invalid $(LREF DateTime).
983      +/
984     @property void minute(int minute) @safe pure
985     {
986         _tod.minute = minute;
987     }
988 
989     @safe unittest
990     {
991         assertThrown!DateTimeException((){DateTime.init.minute = 60;}());
992 
993         auto dt = DateTime.init;
994         dt.minute = 30;
995         assert(dt == DateTime(1, 1, 1, 0, 30, 0));
996 
997         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
998         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
999         static assert(!__traits(compiles, cdt.minute = 27));
1000         static assert(!__traits(compiles, idt.minute = 27));
1001     }
1002 
1003 
1004     /++
1005         Seconds past the minute.
1006      +/
1007     @property ubyte second() const @safe pure nothrow @nogc
1008     {
1009         return _tod.second;
1010     }
1011 
1012     @safe unittest
1013     {
1014         assert(DateTime.init.second == 0);
1015         assert(DateTime(1, 1, 1, 0, 0, 33).second == 33);
1016 
1017         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1018         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1019         assert(cdt.second == 33);
1020         assert(idt.second == 33);
1021     }
1022 
1023 
1024     /++
1025         Seconds past the minute.
1026 
1027         Params:
1028             second = The second to set this $(LREF DateTime)'s second to.
1029 
1030         Throws:
1031             $(REF DateTimeException,std,datetime,date) if the given seconds
1032             would result in an invalid $(LREF DateTime).
1033      +/
1034     @property void second(int second) @safe pure
1035     {
1036         _tod.second = second;
1037     }
1038 
1039     @safe unittest
1040     {
1041         assertThrown!DateTimeException((){DateTime.init.second = 60;}());
1042 
1043         auto dt = DateTime.init;
1044         dt.second = 33;
1045         assert(dt == DateTime(1, 1, 1, 0, 0, 33));
1046 
1047         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1048         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1049         static assert(!__traits(compiles, cdt.second = 27));
1050         static assert(!__traits(compiles, idt.second = 27));
1051     }
1052 
1053 
1054     /++
1055         Adds the given number of years or months to this $(LREF DateTime),
1056         mutating it. A negative number will subtract.
1057 
1058         Note that if day overflow is allowed, and the date with the adjusted
1059         year/month overflows the number of days in the new month, then the month
1060         will be incremented by one, and the day set to the number of days
1061         overflowed. (e.g. if the day were 31 and the new month were June, then
1062         the month would be incremented to July, and the new day would be 1). If
1063         day overflow is not allowed, then the day will be set to the last valid
1064         day in the month (e.g. June 31st would become June 30th).
1065 
1066         Params:
1067             units         = The type of units to add ("years" or "months").
1068             value         = The number of months or years to add to this
1069                             $(LREF DateTime).
1070             allowOverflow = Whether the days should be allowed to overflow,
1071                             causing the month to increment.
1072 
1073         Returns:
1074             A reference to the `DateTime` (`this`).
1075       +/
1076     ref DateTime add(string units)
1077                     (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1078     if (units == "years" || units == "months")
1079     {
1080         _date.add!units(value, allowOverflow);
1081         return this;
1082     }
1083 
1084     ///
1085     @safe unittest
1086     {
1087         auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
1088         dt1.add!"months"(11);
1089         assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
1090 
1091         auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
1092         dt2.add!"months"(-11);
1093         assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
1094 
1095         auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
1096         dt3.add!"years"(1);
1097         assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
1098 
1099         auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
1100         dt4.add!"years"(1, AllowDayOverflow.no);
1101         assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
1102     }
1103 
1104     @safe unittest
1105     {
1106         auto dt = DateTime(2000, 1, 31);
1107         dt.add!"years"(7).add!"months"(-4);
1108         assert(dt == DateTime(2006, 10, 1));
1109 
1110         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1111         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1112         static assert(!__traits(compiles, cdt.add!"years"(4)));
1113         static assert(!__traits(compiles, idt.add!"years"(4)));
1114         static assert(!__traits(compiles, cdt.add!"months"(4)));
1115         static assert(!__traits(compiles, idt.add!"months"(4)));
1116     }
1117 
1118 
1119     /++
1120         Adds the given number of years or months to this $(LREF DateTime),
1121         mutating it. A negative number will subtract.
1122 
1123         The difference between rolling and adding is that rolling does not
1124         affect larger units. Rolling a $(LREF DateTime) 12 months
1125         gets the exact same $(LREF DateTime). However, the days can still be
1126         affected due to the differing number of days in each month.
1127 
1128         Because there are no units larger than years, there is no difference
1129         between adding and rolling years.
1130 
1131         Params:
1132             units         = The type of units to add ("years" or "months").
1133             value         = The number of months or years to add to this
1134                             $(LREF DateTime).
1135             allowOverflow = Whether the days should be allowed to overflow,
1136                             causing the month to increment.
1137 
1138         Returns:
1139             A reference to the `DateTime` (`this`).
1140       +/
1141     ref DateTime roll(string units)
1142                      (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1143     if (units == "years" || units == "months")
1144     {
1145         _date.roll!units(value, allowOverflow);
1146         return this;
1147     }
1148 
1149     ///
1150     @safe unittest
1151     {
1152         auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
1153         dt1.roll!"months"(1);
1154         assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
1155 
1156         auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
1157         dt2.roll!"months"(-1);
1158         assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
1159 
1160         auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
1161         dt3.roll!"months"(1);
1162         assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
1163 
1164         auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
1165         dt4.roll!"months"(1, AllowDayOverflow.no);
1166         assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
1167 
1168         auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
1169         dt5.roll!"years"(1);
1170         assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
1171 
1172         auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
1173         dt6.roll!"years"(1, AllowDayOverflow.no);
1174         assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
1175     }
1176 
1177     @safe unittest
1178     {
1179         auto dt = DateTime(2000, 1, 31);
1180         dt.roll!"years"(7).roll!"months"(-4);
1181         assert(dt == DateTime(2007, 10, 1));
1182 
1183         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1184         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1185         static assert(!__traits(compiles, cdt.roll!"years"(4)));
1186         static assert(!__traits(compiles, idt.roll!"years"(4)));
1187         static assert(!__traits(compiles, cdt.roll!"months"(4)));
1188         static assert(!__traits(compiles, idt.roll!"months"(4)));
1189     }
1190 
1191 
1192     /++
1193         Adds the given number of units to this $(LREF DateTime), mutating it. A
1194         negative number will subtract.
1195 
1196         The difference between rolling and adding is that rolling does not
1197         affect larger units. For instance, rolling a $(LREF DateTime) one
1198         year's worth of days gets the exact same $(LREF DateTime).
1199 
1200         Accepted units are `"days"`, `"minutes"`, `"hours"`,
1201         `"minutes"`, and `"seconds"`.
1202 
1203         Params:
1204             units = The units to add.
1205             value = The number of $(D_PARAM units) to add to this
1206                     $(LREF DateTime).
1207 
1208         Returns:
1209             A reference to the `DateTime` (`this`).
1210       +/
1211     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1212     if (units == "days")
1213     {
1214         _date.roll!"days"(value);
1215         return this;
1216     }
1217 
1218     ///
1219     @safe unittest
1220     {
1221         auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
1222         dt1.roll!"days"(1);
1223         assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
1224         dt1.roll!"days"(365);
1225         assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
1226         dt1.roll!"days"(-32);
1227         assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
1228 
1229         auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
1230         dt2.roll!"hours"(1);
1231         assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
1232 
1233         auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
1234         dt3.roll!"seconds"(-1);
1235         assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
1236     }
1237 
1238     @safe unittest
1239     {
1240         auto dt = DateTime(2000, 1, 31);
1241         dt.roll!"days"(7).roll!"days"(-4);
1242         assert(dt == DateTime(2000, 1, 3));
1243 
1244         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1245         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1246         static assert(!__traits(compiles, cdt.roll!"days"(4)));
1247         static assert(!__traits(compiles, idt.roll!"days"(4)));
1248     }
1249 
1250 
1251     /// ditto
1252     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1253     if (units == "hours" ||
1254         units == "minutes" ||
1255         units == "seconds")
1256     {
1257         _tod.roll!units(value);
1258         return this;
1259     }
1260 
1261     // Test roll!"hours"().
1262     @safe unittest
1263     {
1264         static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
1265         {
1266             orig.roll!"hours"(hours);
1267             assert(orig == expected);
1268         }
1269 
1270         // Test A.D.
1271         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1273         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1274                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1275         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1276                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1277         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1278                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1279         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1280                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1281         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1282                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1283         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1284                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1285         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1286                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1287         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1288                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1289         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1290                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1291         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1292                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1293         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1294                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1295         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1296                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1297         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1298                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1299         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1300                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1301         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1302                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1303         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1304                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1305         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1306                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1307         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1308                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1309         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1310                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1311         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1312                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1313         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1314                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1315         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1316                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1317         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1318                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1319         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1320                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1321         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1322                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1323 
1324         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1325                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1326         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1327                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1328         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1329                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1330         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1331                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1332         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1333                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1334         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1335                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1336         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1337                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1338         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1339                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1340         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1341                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1342         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1343                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1344         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1345                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1346         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1347                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1348         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1349                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1350         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1351                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1352         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1353                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1354         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1355                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1356         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1357                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1358         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1359                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1360         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1361                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1362         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1363                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1364         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1365                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1366         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1367                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1368         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1369                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1370         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1371                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1372         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1373                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1374 
1375         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1376                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1377         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1378                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1379         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1380                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1381 
1382         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1383                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1384         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1385                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1386         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1387                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1388 
1389         testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1390                DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33)));
1391         testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1392                DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33)));
1393 
1394         testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1,
1395                DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33)));
1396         testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1397                DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33)));
1398 
1399         testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25,
1400                DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33)));
1401         testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25,
1402                DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33)));
1403 
1404         testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1405                DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33)));
1406         testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1407                DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33)));
1408 
1409         // Test B.C.
1410         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1411                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1412         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1413                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1414         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1415                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1416         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1417                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1418         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1419                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1420         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1421                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1422         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1423                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1424         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1425                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1426         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1427                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1428         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1429                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1430         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1431                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1432         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1433                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1434         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1435                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1436         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1437                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1438         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1439                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1440         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1441                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1442         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1443                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1444         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1445                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1446         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1447                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1448         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1449                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1450         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1451                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1452         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1453                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1454         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1455                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1456         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1457                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1458         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1459                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1460         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1461                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1462 
1463         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1464                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1465         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1466                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1467         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1468                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1469         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1470                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1471         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1472                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1473         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1474                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1475         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1476                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1477         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1478                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1479         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1480                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1481         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1482                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1483         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1484                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1485         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1486                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1487         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1488                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1489         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1490                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1491         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1492                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1493         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1494                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1495         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1496                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1497         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1498                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1499         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1500                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1501         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1502                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1503         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1504                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1505         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1506                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1507         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1508                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1509         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1510                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1511         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1512                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1513 
1514         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1515                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1516         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1517                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1518         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1519                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1520 
1521         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1522                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1523         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1524                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1525         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1526                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1527 
1528         testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1529                DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33)));
1530         testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1531                DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33)));
1532 
1533         testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1,
1534                DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33)));
1535         testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1536                DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33)));
1537 
1538         testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25,
1539                DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33)));
1540         testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25,
1541                DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33)));
1542 
1543         testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1544                DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33)));
1545         testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1546                DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33)));
1547 
1548         // Test Both
1549         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546,
1550                DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33)));
1551         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546,
1552                DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33)));
1553 
1554         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1555         dt.roll!"hours"(27).roll!"hours"(-9);
1556         assert(dt == DateTime(2000, 1, 31, 3, 7, 6));
1557 
1558         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1559         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1560         static assert(!__traits(compiles, cdt.roll!"hours"(4)));
1561         static assert(!__traits(compiles, idt.roll!"hours"(4)));
1562     }
1563 
1564     // Test roll!"minutes"().
1565     @safe unittest
1566     {
1567         static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
1568         {
1569             orig.roll!"minutes"(minutes);
1570             assert(orig == expected);
1571         }
1572 
1573         // Test A.D.
1574         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1575                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1576         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1577                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1578         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1579                DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33)));
1580         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1581                DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33)));
1582         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1583                DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33)));
1584         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1585                DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33)));
1586         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1587                DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33)));
1588         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1589                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1590         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1591                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1592         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1593                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1594         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1595                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1596         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1597                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1598         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1599                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1600         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1601                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1602         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1603                DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33)));
1604 
1605         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1606                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1607         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1608                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1609         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1610                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1611         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1612                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1613         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1614                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1615         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1616                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1617         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1618                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1619         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1620                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1621 
1622         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1623                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1624         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1625                DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33)));
1626         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1627                DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33)));
1628         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1629                DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33)));
1630         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1631                DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33)));
1632         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1633                DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33)));
1634         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1635                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1636         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1637                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1638         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1639                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1640         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1641                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1642         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1643                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1644         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1645                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1646         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1647                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1648         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1649                DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33)));
1650 
1651         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1652                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1653         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1654                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1655         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1656                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1657         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1658                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1659         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1660                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1661         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1662                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1663         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1664                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1665         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1666                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1667 
1668         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1669                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1670         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1671                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1672         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1673                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1674 
1675         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1676                DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33)));
1677         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1678                DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)));
1679         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1680                DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33)));
1681 
1682         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1683                DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33)));
1684         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1685                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)));
1686         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1687                DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33)));
1688 
1689         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1690                DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33)));
1691         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1692                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)));
1693         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1694                DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33)));
1695 
1696         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1,
1697                DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33)));
1698         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0,
1699                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)));
1700         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1,
1701                DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33)));
1702 
1703         // Test B.C.
1704         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1705                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1706         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1707                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1708         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1709                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33)));
1710         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1711                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33)));
1712         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1713                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33)));
1714         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1715                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33)));
1716         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1717                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33)));
1718         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1719                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1720         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1721                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1722         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1723                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1724         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1725                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1726         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1727                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1728         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1729                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1730         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1731                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1732         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1733                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33)));
1734 
1735         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1736                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1737         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1738                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1739         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1740                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1741         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1742                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1743         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1744                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1745         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1746                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1747         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1748                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1749         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1750                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1751 
1752         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1753                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1754         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1755                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33)));
1756         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1757                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33)));
1758         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1759                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33)));
1760         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1761                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33)));
1762         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1763                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33)));
1764         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1765                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1766         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1767                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1768         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1769                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1770         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1771                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1772         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1773                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1774         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1775                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1776         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1777                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1778         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1779                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33)));
1780 
1781         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1782                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1783         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1784                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1785         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1786                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1787         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1788                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1789         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1790                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1791         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1792                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1793         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1794                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1795         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1796                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1797 
1798         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1799                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1800         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1801                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1802         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1803                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1804 
1805         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1806                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33)));
1807         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1808                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)));
1809         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1810                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33)));
1811 
1812         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1813                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33)));
1814         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1815                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)));
1816         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1817                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33)));
1818 
1819         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1820                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33)));
1821         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1822                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)));
1823         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1824                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33)));
1825 
1826         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1,
1827                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33)));
1828         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0,
1829                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)));
1830         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1,
1831                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33)));
1832 
1833         // Test Both
1834         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
1835                DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0)));
1836         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1,
1837                DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0)));
1838 
1839         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
1840                DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0)));
1841         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1,
1842                DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0)));
1843 
1844         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760,
1845                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
1846         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760,
1847                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1848 
1849         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782,
1850                DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33)));
1851         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782,
1852                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1853 
1854         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1855         dt.roll!"minutes"(92).roll!"minutes"(-292);
1856         assert(dt == DateTime(2000, 1, 31, 9, 47, 6));
1857 
1858         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1859         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1860         static assert(!__traits(compiles, cdt.roll!"minutes"(4)));
1861         static assert(!__traits(compiles, idt.roll!"minutes"(4)));
1862     }
1863 
1864     // Test roll!"seconds"().
1865     @safe unittest
1866     {
1867         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
1868         {
1869             orig.roll!"seconds"(seconds);
1870             assert(orig == expected);
1871         }
1872 
1873         // Test A.D.
1874         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1875                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1876         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1877                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1878         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1879                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35)));
1880         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1881                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36)));
1882         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1883                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37)));
1884         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1885                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38)));
1886         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1887                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43)));
1888         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1889                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48)));
1890         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
1891                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1892         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
1893                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1894         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1895                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3)));
1896         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
1897                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1898         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1899                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1900         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
1901                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1902 
1903         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
1904                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1905         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
1906                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1907         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
1908                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1909         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
1910                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1911         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
1912                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1913         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
1914                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1915         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
1916                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1917         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
1918                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1919 
1920         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1921                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1922         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1923                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31)));
1924         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1925                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30)));
1926         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1927                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29)));
1928         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1929                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28)));
1930         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1931                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23)));
1932         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1933                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18)));
1934         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
1935                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1936         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
1937                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1938         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
1939                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58)));
1940         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
1941                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1942         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1943                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1944         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
1945                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1946 
1947         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
1948                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1949         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
1950                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1951         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
1952                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1953 
1954         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
1955                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1)));
1956         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
1957                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)));
1958         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
1959                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59)));
1960 
1961         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
1962                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1)));
1963         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
1964                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)));
1965         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
1966                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59)));
1967 
1968         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
1969                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0)));
1970         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
1971                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)));
1972         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
1973                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58)));
1974 
1975         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1,
1976                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0)));
1977         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0,
1978                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)));
1979         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1,
1980                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58)));
1981 
1982         // Test B.C.
1983         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1984                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1985         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1986                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1987         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1988                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35)));
1989         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1990                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36)));
1991         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1992                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37)));
1993         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1994                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38)));
1995         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1996                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43)));
1997         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1998                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48)));
1999         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
2000                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2001         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
2002                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2003         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
2004                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3)));
2005         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
2006                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2007         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
2008                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2009         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
2010                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2011 
2012         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
2013                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2014         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
2015                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2016         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
2017                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2018         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
2019                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2020         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
2021                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2022         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
2023                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2024         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
2025                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2026         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
2027                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2028 
2029         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
2030                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2031         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
2032                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31)));
2033         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
2034                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30)));
2035         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
2036                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29)));
2037         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
2038                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28)));
2039         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
2040                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23)));
2041         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
2042                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18)));
2043         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
2044                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2045         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
2046                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2047         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
2048                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58)));
2049         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
2050                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2051         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
2052                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2053         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
2054                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2055 
2056         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
2057                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2058         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
2059                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2060         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
2061                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2062 
2063         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
2064                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1)));
2065         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
2066                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)));
2067         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
2068                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59)));
2069 
2070         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
2071                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1)));
2072         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
2073                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)));
2074         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
2075                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59)));
2076 
2077         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
2078                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0)));
2079         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
2080                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)));
2081         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
2082                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58)));
2083 
2084         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1,
2085                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0)));
2086         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0,
2087                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)));
2088         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1,
2089                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58)));
2090 
2091         // Test Both
2092         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
2093                DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59)));
2094         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1,
2095                DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)));
2096 
2097         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
2098                DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59)));
2099         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1,
2100                DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)));
2101 
2102         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L,
2103                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
2104         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L,
2105                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2106 
2107         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L,
2108                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50)));
2109         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L,
2110                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2111 
2112         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2113         dt.roll!"seconds"(92).roll!"seconds"(-292);
2114         assert(dt == DateTime(2000, 1, 31, 9, 7, 46));
2115 
2116         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2117         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2118         static assert(!__traits(compiles, cdt.roll!"seconds"(4)));
2119         static assert(!__traits(compiles, idt.roll!"seconds"(4)));
2120     }
2121 
2122 
2123     import core.time : Duration;
2124     /++
2125         Gives the result of adding or subtracting a $(REF Duration, core,time)
2126         from this $(LREF DateTime).
2127 
2128         The legal types of arithmetic for $(LREF DateTime) using this operator
2129         are
2130 
2131         $(BOOKTABLE,
2132         $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
2133         $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
2134         $(TR $(TD Duration) $(TD +) $(TD DateTime) $(TD -->) $(TD DateTime))
2135         )
2136 
2137         Params:
2138             duration = The $(REF Duration, core,time) to add to or subtract from
2139                        this $(LREF DateTime).
2140       +/
2141     DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
2142     if (op == "+" || op == "-")
2143     {
2144         DateTime retval = this;
2145         immutable seconds = duration.total!"seconds";
2146         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
2147     }
2148 
2149     /// ditto
2150     DateTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
2151     if (op == "+")
2152     {
2153         return this + duration;
2154     }
2155 
2156 
2157     ///
2158     @safe unittest
2159     {
2160         import core.time : hours, seconds;
2161 
2162         assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
2163                DateTime(2016, 1, 1, 0, 0, 0));
2164 
2165         assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
2166                DateTime(2016, 1, 1, 0, 59, 59));
2167 
2168         assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
2169                DateTime(2015, 12, 31, 23, 59, 59));
2170 
2171         assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
2172                DateTime(2015, 12, 31, 23, 59, 59));
2173 
2174         assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
2175                hours(1) + DateTime(2015, 12, 31, 23, 59, 59));
2176     }
2177 
2178     @safe unittest
2179     {
2180         import core.time : dur;
2181 
2182         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2183 
2184         assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2185         assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2186         assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2187         assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2188 
2189         assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2190         assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2191         assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2192         assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2193         assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2194         assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2195         assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2196         assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2197         assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2198         assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2199         assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2200         assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2201 
2202         assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2203         assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2204         assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2205         assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2206 
2207         assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2208         assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2209         assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2210         assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2211         assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2212         assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2213         assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2214         assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2215         assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2216         assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2217         assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2218         assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2219 
2220         auto duration = dur!"seconds"(12);
2221         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2222         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2223         assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2224         assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2225         assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2226         assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2227     }
2228 
2229 
2230     /++
2231         Gives the result of adding or subtracting a duration from this
2232         $(LREF DateTime), as well as assigning the result to this
2233         $(LREF DateTime).
2234 
2235         The legal types of arithmetic for $(LREF DateTime) using this operator
2236         are
2237 
2238         $(BOOKTABLE,
2239         $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime))
2240         $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime))
2241         )
2242 
2243         Params:
2244             duration = The duration to add to or subtract from this
2245                        $(LREF DateTime).
2246       +/
2247     ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
2248     if (op == "+" || op == "-")
2249     {
2250         import core.time : convert;
2251         import std.format : format;
2252 
2253         DateTime retval = this;
2254         immutable hnsecs = duration.total!"hnsecs";
2255 
2256         mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
2257     }
2258 
2259     @safe unittest
2260     {
2261         import core.time : dur;
2262         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
2263                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2264         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
2265                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2266         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) ==
2267                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2268         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) ==
2269                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2270 
2271         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) ==
2272                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2273         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) ==
2274                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2275         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) ==
2276                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2277         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) ==
2278                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2279         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) ==
2280                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2281         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) ==
2282                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2283         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) ==
2284                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2285         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) ==
2286                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2287         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) ==
2288                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2289         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) ==
2290                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2291         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) ==
2292                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) ==
2294                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2295 
2296         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) ==
2297                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2298         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) ==
2299                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2300         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) ==
2301                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2302         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) ==
2303                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2304 
2305         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) ==
2306                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2307         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) ==
2308                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2309         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) ==
2310                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2311         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) ==
2312                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2313         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) ==
2314                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) ==
2316                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2317         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) ==
2318                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2319         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) ==
2320                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2321         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) ==
2322                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2323         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) ==
2324                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2325         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) ==
2326                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2327         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) ==
2328                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2329 
2330         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2331         (dt += dur!"seconds"(92)) -= dur!"days"(-500);
2332         assert(dt == DateTime(2001, 6, 14, 9, 8, 38));
2333 
2334         auto duration = dur!"seconds"(12);
2335         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2336         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2337         static assert(!__traits(compiles, cdt += duration));
2338         static assert(!__traits(compiles, idt += duration));
2339         static assert(!__traits(compiles, cdt -= duration));
2340         static assert(!__traits(compiles, idt -= duration));
2341     }
2342 
2343 
2344     /++
2345         Gives the difference between two $(LREF DateTime)s.
2346 
2347         The legal types of arithmetic for $(LREF DateTime) using this operator are
2348 
2349         $(BOOKTABLE,
2350         $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
2351         )
2352       +/
2353     Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
2354     if (op == "-")
2355     {
2356         immutable dateResult = _date - rhs.date;
2357         immutable todResult = _tod - rhs._tod;
2358 
2359         import core.time : dur;
2360         return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
2361     }
2362 
2363     @safe unittest
2364     {
2365         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
2366 
2367         import core.time : dur;
2368         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
2369                dur!"seconds"(31_536_000));
2370         assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2371                dur!"seconds"(-31_536_000));
2372 
2373         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2374                dur!"seconds"(26_78_400));
2375         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) ==
2376                dur!"seconds"(-26_78_400));
2377 
2378         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) ==
2379                dur!"seconds"(86_400));
2380         assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2381                dur!"seconds"(-86_400));
2382 
2383         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) ==
2384                dur!"seconds"(3600));
2385         assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2386                dur!"seconds"(-3600));
2387 
2388         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2389                dur!"seconds"(60));
2390         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) ==
2391                dur!"seconds"(-60));
2392 
2393         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2394                dur!"seconds"(1));
2395         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) ==
2396                dur!"seconds"(-1));
2397 
2398         assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033));
2399         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033));
2400         assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367));
2401         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367));
2402 
2403         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2404         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2405         assert(dt - dt == Duration.zero);
2406         assert(cdt - dt == Duration.zero);
2407         assert(idt - dt == Duration.zero);
2408 
2409         assert(dt - cdt == Duration.zero);
2410         assert(cdt - cdt == Duration.zero);
2411         assert(idt - cdt == Duration.zero);
2412 
2413         assert(dt - idt == Duration.zero);
2414         assert(cdt - idt == Duration.zero);
2415         assert(idt - idt == Duration.zero);
2416     }
2417 
2418 
2419     /++
2420         Returns the difference between the two $(LREF DateTime)s in months.
2421 
2422         To get the difference in years, subtract the year property
2423         of two $(LREF DateTime)s. To get the difference in days or weeks,
2424         subtract the $(LREF DateTime)s themselves and use the
2425         $(REF Duration, core,time) that results. Because converting between
2426         months and smaller units requires a specific date (which
2427         $(REF Duration, core,time)s don't have), getting the difference in
2428         months requires some math using both the year and month properties, so
2429         this is a convenience function for getting the difference in months.
2430 
2431         Note that the number of days in the months or how far into the month
2432         either date is is irrelevant. It is the difference in the month property
2433         combined with the difference in years * 12. So, for instance,
2434         December 31st and January 1st are one month apart just as December 1st
2435         and January 31st are one month apart.
2436 
2437         Params:
2438             rhs = The $(LREF DateTime) to subtract from this one.
2439       +/
2440     int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
2441     {
2442         return _date.diffMonths(rhs._date);
2443     }
2444 
2445     ///
2446     @safe unittest
2447     {
2448         assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
2449                    DateTime(1999, 1, 31, 23, 59, 59)) == 1);
2450 
2451         assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
2452                    DateTime(1999, 2, 1, 12, 3, 42)) == -1);
2453 
2454         assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
2455                    DateTime(1999, 1, 1, 2, 4, 7)) == 2);
2456 
2457         assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
2458                    DateTime(1999, 3, 31, 0, 30, 58)) == -2);
2459     }
2460 
2461     @safe unittest
2462     {
2463         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2464         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2465         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2466         assert(dt.diffMonths(dt) == 0);
2467         assert(cdt.diffMonths(dt) == 0);
2468         assert(idt.diffMonths(dt) == 0);
2469 
2470         assert(dt.diffMonths(cdt) == 0);
2471         assert(cdt.diffMonths(cdt) == 0);
2472         assert(idt.diffMonths(cdt) == 0);
2473 
2474         assert(dt.diffMonths(idt) == 0);
2475         assert(cdt.diffMonths(idt) == 0);
2476         assert(idt.diffMonths(idt) == 0);
2477     }
2478 
2479 
2480     /++
2481         Whether this $(LREF DateTime) is in a leap year.
2482      +/
2483     @property bool isLeapYear() const @safe pure nothrow @nogc
2484     {
2485         return _date.isLeapYear;
2486     }
2487 
2488     @safe unittest
2489     {
2490         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2491         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2492         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2493         assert(!dt.isLeapYear);
2494         assert(!cdt.isLeapYear);
2495         assert(!idt.isLeapYear);
2496     }
2497 
2498 
2499     /++
2500         Day of the week this $(LREF DateTime) is on.
2501       +/
2502     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
2503     {
2504         return _date.dayOfWeek;
2505     }
2506 
2507     @safe unittest
2508     {
2509         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2510         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2511         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2512         assert(dt.dayOfWeek == DayOfWeek.tue);
2513         assert(cdt.dayOfWeek == DayOfWeek.tue);
2514         assert(idt.dayOfWeek == DayOfWeek.tue);
2515     }
2516 
2517 
2518     /++
2519         Day of the year this $(LREF DateTime) is on.
2520       +/
2521     @property ushort dayOfYear() const @safe pure nothrow @nogc
2522     {
2523         return _date.dayOfYear;
2524     }
2525 
2526     ///
2527     @safe unittest
2528     {
2529         assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
2530         assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
2531         assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
2532     }
2533 
2534     @safe unittest
2535     {
2536         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2537         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2538         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2539         assert(dt.dayOfYear == 187);
2540         assert(cdt.dayOfYear == 187);
2541         assert(idt.dayOfYear == 187);
2542     }
2543 
2544 
2545     /++
2546         Day of the year.
2547 
2548         Params:
2549             day = The day of the year to set which day of the year this
2550                   $(LREF DateTime) is on.
2551       +/
2552     @property void dayOfYear(int day) @safe pure
2553     {
2554         _date.dayOfYear = day;
2555     }
2556 
2557     @safe unittest
2558     {
2559         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2560         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2561         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2562         dt.dayOfYear = 12;
2563         assert(dt.dayOfYear == 12);
2564         static assert(!__traits(compiles, cdt.dayOfYear = 12));
2565         static assert(!__traits(compiles, idt.dayOfYear = 12));
2566     }
2567 
2568 
2569     /++
2570         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2571      +/
2572     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
2573     {
2574         return _date.dayOfGregorianCal;
2575     }
2576 
2577     ///
2578     @safe unittest
2579     {
2580         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
2581         assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
2582         assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
2583 
2584         assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
2585         assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
2586         assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
2587 
2588         assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
2589         assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
2590     }
2591 
2592     @safe unittest
2593     {
2594         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2595         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2596         assert(cdt.dayOfGregorianCal == 729_941);
2597         assert(idt.dayOfGregorianCal == 729_941);
2598     }
2599 
2600 
2601     /++
2602         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2603         Setting this property does not affect the time portion of
2604         $(LREF DateTime).
2605 
2606         Params:
2607             days = The day of the Gregorian Calendar to set this $(LREF DateTime)
2608                    to.
2609      +/
2610     @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
2611     {
2612         _date.dayOfGregorianCal = days;
2613     }
2614 
2615     ///
2616     @safe unittest
2617     {
2618         auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
2619         dt.dayOfGregorianCal = 1;
2620         assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
2621 
2622         dt.dayOfGregorianCal = 365;
2623         assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
2624 
2625         dt.dayOfGregorianCal = 366;
2626         assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
2627 
2628         dt.dayOfGregorianCal = 0;
2629         assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
2630 
2631         dt.dayOfGregorianCal = -365;
2632         assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
2633 
2634         dt.dayOfGregorianCal = -366;
2635         assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
2636 
2637         dt.dayOfGregorianCal = 730_120;
2638         assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
2639 
2640         dt.dayOfGregorianCal = 734_137;
2641         assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
2642     }
2643 
2644     @safe unittest
2645     {
2646         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2647         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2648         static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7));
2649         static assert(!__traits(compiles, idt.dayOfGregorianCal = 7));
2650     }
2651 
2652 
2653     /++
2654         The ISO 8601 week of the year that this $(LREF DateTime) is in.
2655 
2656         See_Also:
2657             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2658       +/
2659     @property ubyte isoWeek() const @safe pure nothrow
2660     {
2661         return _date.isoWeek;
2662     }
2663 
2664     @safe unittest
2665     {
2666         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2667         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2668         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2669         assert(dt.isoWeek == 27);
2670         assert(cdt.isoWeek == 27);
2671         assert(idt.isoWeek == 27);
2672     }
2673 
2674 
2675     /++
2676         The year of the ISO 8601 week calendar that this $(LREF DateTime) is in.
2677 
2678         See_Also:
2679             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2680       +/
2681     @property short isoWeekYear() const @safe pure nothrow
2682     {
2683         return _date.isoWeekYear;
2684     }
2685 
2686 
2687     /++
2688         $(LREF DateTime) for the last day in the month that this
2689         $(LREF DateTime) is in. The time portion of endOfMonth is always
2690         23:59:59.
2691       +/
2692     @property DateTime endOfMonth() const @safe pure nothrow
2693     {
2694         try
2695             return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
2696         catch (Exception e)
2697             assert(0, "DateTime constructor threw.");
2698     }
2699 
2700     ///
2701     @safe unittest
2702     {
2703         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
2704                DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
2705 
2706         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
2707                DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
2708 
2709         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
2710                DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
2711 
2712         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
2713                DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
2714     }
2715 
2716     @safe unittest
2717     {
2718         // Test A.D.
2719         assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59));
2720         assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59));
2721         assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59));
2722         assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59));
2723         assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59));
2724         assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59));
2725         assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59));
2726         assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2727         assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59));
2728         assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59));
2729         assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59));
2730         assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59));
2731         assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59));
2732 
2733         // Test B.C.
2734         assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59));
2735         assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59));
2736         assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59));
2737         assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59));
2738         assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59));
2739         assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59));
2740         assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59));
2741         assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59));
2742         assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59));
2743         assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59));
2744         assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59));
2745         assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59));
2746         assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59));
2747 
2748         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2749         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2750         assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2751         assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2752     }
2753 
2754 
2755     /++
2756         The last day in the month that this $(LREF DateTime) is in.
2757       +/
2758     @property ubyte daysInMonth() const @safe pure nothrow @nogc
2759     {
2760         return _date.daysInMonth;
2761     }
2762 
2763     ///
2764     @safe unittest
2765     {
2766         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
2767         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
2768         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
2769         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
2770     }
2771 
2772     @safe unittest
2773     {
2774         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2775         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2776         assert(cdt.daysInMonth == 31);
2777         assert(idt.daysInMonth == 31);
2778     }
2779 
2780 
2781     /++
2782         Whether the current year is a date in A.D.
2783       +/
2784     @property bool isAD() const @safe pure nothrow @nogc
2785     {
2786         return _date.isAD;
2787     }
2788 
2789     ///
2790     @safe unittest
2791     {
2792         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
2793         assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
2794         assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
2795         assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
2796     }
2797 
2798     @safe unittest
2799     {
2800         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2801         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2802         assert(cdt.isAD);
2803         assert(idt.isAD);
2804     }
2805 
2806 
2807     /++
2808         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
2809         $(LREF DateTime) at the given time. For example, prior to noon,
2810         1996-03-31 would be the Julian day number 2_450_173, so this function
2811         returns 2_450_173, while from noon onward, the julian day number would
2812         be 2_450_174, so this function returns 2_450_174.
2813       +/
2814     @property long julianDay() const @safe pure nothrow @nogc
2815     {
2816         if (_tod._hour < 12)
2817             return _date.julianDay - 1;
2818         else
2819             return _date.julianDay;
2820     }
2821 
2822     @safe unittest
2823     {
2824         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1);
2825         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0);
2826 
2827         assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424);
2828         assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425);
2829 
2830         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425);
2831         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426);
2832 
2833         assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160);
2834         assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161);
2835 
2836         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000);
2837         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001);
2838 
2839         assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973);
2840         assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974);
2841 
2842         assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173);
2843         assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174);
2844 
2845         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432);
2846         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433);
2847 
2848         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2849         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2850         assert(cdt.julianDay == 2_451_366);
2851         assert(idt.julianDay == 2_451_366);
2852     }
2853 
2854 
2855     /++
2856         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
2857         time on this date (since, the modified Julian day changes at midnight).
2858       +/
2859     @property long modJulianDay() const @safe pure nothrow @nogc
2860     {
2861         return _date.modJulianDay;
2862     }
2863 
2864     @safe unittest
2865     {
2866         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0);
2867         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0);
2868 
2869         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432);
2870         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432);
2871 
2872         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2873         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2874         assert(cdt.modJulianDay == 51_365);
2875         assert(idt.modJulianDay == 51_365);
2876     }
2877 
2878 
2879     /++
2880         Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`.
2881         If `writer` is set, the resulting string will be written directly to it.
2882 
2883         Params:
2884             writer = A `char` accepting
2885             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2886         Returns:
2887             A `string` when not using an output range; `void` otherwise.
2888       +/
2889     string toISOString() const @safe pure nothrow
2890     {
2891         import std.array : appender;
2892         auto w = appender!string();
2893         w.reserve(18);
2894         try
2895             toISOString(w);
2896         catch (Exception e)
2897             assert(0, "toISOString() threw.");
2898         return w.data;
2899     }
2900 
2901     /// ditto
2902     void toISOString(W)(ref W writer) const
2903     if (isOutputRange!(W, char))
2904     {
2905         import std.format.write : formattedWrite;
2906         _date.toISOString(writer);
2907         formattedWrite!("T%02d%02d%02d")(
2908             writer,
2909             _tod._hour,
2910             _tod._minute,
2911             _tod._second
2912         );
2913     }
2914 
2915     ///
2916     @safe unittest
2917     {
2918         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
2919                "20100704T070612");
2920 
2921         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
2922                "19981225T021500");
2923 
2924         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
2925                "00000105T230959");
2926 
2927         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
2928                "-00040105T000002");
2929     }
2930 
2931     @safe unittest
2932     {
2933         // Test A.D.
2934         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000");
2935         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612");
2936         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459");
2937         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959");
2938         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101");
2939 
2940         // Test B.C.
2941         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204");
2942         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000");
2943         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612");
2944         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459");
2945         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959");
2946         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101");
2947 
2948         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2949         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2950         assert(cdt.toISOString() == "19990706T123033");
2951         assert(idt.toISOString() == "19990706T123033");
2952     }
2953 
2954 
2955     /++
2956         Converts this $(LREF DateTime) to a string with the format
2957         `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting
2958         string will be written directly to it.
2959 
2960         Params:
2961             writer = A `char` accepting
2962             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2963         Returns:
2964             A `string` when not using an output range; `void` otherwise.
2965       +/
2966     string toISOExtString() const @safe pure nothrow
2967     {
2968         import std.array : appender;
2969         auto w = appender!string();
2970         w.reserve(20);
2971         try
2972             toISOExtString(w);
2973         catch (Exception e)
2974             assert(0, "toISOExtString() threw.");
2975         return w.data;
2976     }
2977 
2978     /// ditto
2979     void toISOExtString(W)(ref W writer) const
2980     if (isOutputRange!(W, char))
2981     {
2982         import std.format.write : formattedWrite;
2983         _date.toISOExtString(writer);
2984         formattedWrite!("T%02d:%02d:%02d")(
2985             writer,
2986             _tod._hour,
2987             _tod._minute,
2988             _tod._second
2989         );
2990     }
2991 
2992     ///
2993     @safe unittest
2994     {
2995         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
2996                "2010-07-04T07:06:12");
2997 
2998         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
2999                "1998-12-25T02:15:00");
3000 
3001         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
3002                "0000-01-05T23:09:59");
3003 
3004         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
3005                "-0004-01-05T00:00:02");
3006     }
3007 
3008     @safe unittest
3009     {
3010         // Test A.D.
3011         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00");
3012         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12");
3013         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59");
3014         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59");
3015         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01");
3016 
3017         // Test B.C.
3018         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04");
3019         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00");
3020         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12");
3021         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59");
3022         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59");
3023         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01");
3024 
3025         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3026         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3027         assert(cdt.toISOExtString() == "1999-07-06T12:30:33");
3028         assert(idt.toISOExtString() == "1999-07-06T12:30:33");
3029     }
3030 
3031     /++
3032         Converts this $(LREF DateTime) to a string with the format
3033         `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting
3034         string will be written directly to it.
3035 
3036         Params:
3037             writer = A `char` accepting
3038             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3039         Returns:
3040             A `string` when not using an output range; `void` otherwise.
3041       +/
3042     string toSimpleString() const @safe pure nothrow
3043     {
3044         import std.array : appender;
3045         auto w = appender!string();
3046         w.reserve(22);
3047         try
3048             toSimpleString(w);
3049         catch (Exception e)
3050             assert(0, "toSimpleString() threw.");
3051         return w.data;
3052     }
3053 
3054     /// ditto
3055     void toSimpleString(W)(ref W writer) const
3056     if (isOutputRange!(W, char))
3057     {
3058         import std.format.write : formattedWrite;
3059         _date.toSimpleString(writer);
3060         formattedWrite!(" %02d:%02d:%02d")(
3061             writer,
3062             _tod._hour,
3063             _tod._minute,
3064             _tod._second
3065         );
3066     }
3067 
3068     ///
3069     @safe unittest
3070     {
3071         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
3072                "2010-Jul-04 07:06:12");
3073 
3074         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
3075                "1998-Dec-25 02:15:00");
3076 
3077         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
3078                "0000-Jan-05 23:09:59");
3079 
3080         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
3081                "-0004-Jan-05 00:00:02");
3082     }
3083 
3084     @safe unittest
3085     {
3086         // Test A.D.
3087         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00");
3088         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12");
3089         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59");
3090         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59");
3091         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01");
3092 
3093         // Test B.C.
3094         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04");
3095         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00");
3096         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12");
3097         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59");
3098         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59");
3099         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01");
3100 
3101         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3102         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3103         assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33");
3104         assert(idt.toSimpleString() == "1999-Jul-06 12:30:33");
3105     }
3106 
3107 
3108     /++
3109         Converts this $(LREF DateTime) to a string.
3110 
3111         This function exists to make it easy to convert a $(LREF DateTime) to a
3112         string for code that does not care what the exact format is - just that
3113         it presents the information in a clear manner. It also makes it easy to
3114         simply convert a $(LREF DateTime) to a string when using functions such
3115         as `to!string`, `format`, or `writeln` which use toString to convert
3116         user-defined types. So, it is unlikely that much code will call
3117         toString directly.
3118 
3119         The format of the string is purposefully unspecified, and code that
3120         cares about the format of the string should use `toISOString`,
3121         `toISOExtString`, `toSimpleString`, or some other custom formatting
3122         function that explicitly generates the format that the code needs. The
3123         reason is that the code is then clear about what format it's using,
3124         making it less error-prone to maintain the code and interact with other
3125         software that consumes the generated strings. It's for this same reason
3126         that $(LREF DateTime) has no `fromString` function, whereas it does have
3127         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
3128 
3129         The format returned by toString may or may not change in the future.
3130       +/
3131     string toString() const @safe pure nothrow
3132     {
3133         return toSimpleString();
3134     }
3135 
3136     @safe unittest
3137     {
3138         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3139         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3140         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3141         assert(dt.toString());
3142         assert(cdt.toString());
3143         assert(idt.toString());
3144     }
3145 
3146     /// ditto
3147     void toString(W)(ref W writer) const
3148     if (isOutputRange!(W, char))
3149     {
3150         toSimpleString(writer);
3151     }
3152 
3153     /++
3154         Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
3155         Whitespace is stripped from the given string.
3156 
3157         Params:
3158             isoString = A string formatted in the ISO format for dates and times.
3159 
3160         Throws:
3161             $(REF DateTimeException,std,datetime,date) if the given string is
3162             not in the ISO format or if the resulting $(LREF DateTime) would not
3163             be valid.
3164       +/
3165     static DateTime fromISOString(S)(scope const S isoString) @safe pure
3166     if (isSomeString!S)
3167     {
3168         import std.algorithm.searching : countUntil;
3169         import std.exception : enforce;
3170         import std.format : format;
3171         import std.string : strip;
3172         import std.utf : byCodeUnit;
3173 
3174         auto str = strip(isoString);
3175 
3176         enforce!DateTimeException(str.length >= 15, format("Invalid format for DateTime.fromISOString %s", isoString));
3177         auto t = str.byCodeUnit.countUntil('T');
3178 
3179         enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromISOString: %s", isoString));
3180 
3181         immutable date = Date.fromISOString(str[0 .. t]);
3182         immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
3183 
3184         return DateTime(date, tod);
3185     }
3186 
3187     ///
3188     @safe unittest
3189     {
3190         assert(DateTime.fromISOString("20100704T070612") ==
3191                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3192 
3193         assert(DateTime.fromISOString("19981225T021500") ==
3194                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3195 
3196         assert(DateTime.fromISOString("00000105T230959") ==
3197                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3198 
3199         assert(DateTime.fromISOString("-00040105T000002") ==
3200                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3201 
3202         assert(DateTime.fromISOString(" 20100704T070612 ") ==
3203                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3204     }
3205 
3206     @safe unittest
3207     {
3208         assertThrown!DateTimeException(DateTime.fromISOString(""));
3209         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3210         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3211         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3212         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3213         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3214 
3215         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3216         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3217         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3218         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3219         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3220 
3221         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3222         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3223         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3224         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3225         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3226         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3227 
3228         assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
3229         assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
3230 
3231         assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3232         assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3233         assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3234         assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3235         assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3236         assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3237         assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3238     }
3239 
3240     // https://issues.dlang.org/show_bug.cgi?id=17801
3241     @safe unittest
3242     {
3243         import std.conv : to;
3244         import std.meta : AliasSeq;
3245         static foreach (C; AliasSeq!(char, wchar, dchar))
3246         {
3247             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3248                 assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
3249         }
3250     }
3251 
3252 
3253     /++
3254         Creates a $(LREF DateTime) from a string with the format
3255         YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string.
3256 
3257         Params:
3258             isoExtString = A string formatted in the ISO Extended format for dates
3259                            and times.
3260 
3261         Throws:
3262             $(REF DateTimeException,std,datetime,date) if the given string is
3263             not in the ISO Extended format or if the resulting $(LREF DateTime)
3264             would not be valid.
3265       +/
3266     static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
3267     if (isSomeString!(S))
3268     {
3269         import std.algorithm.searching : countUntil;
3270         import std.exception : enforce;
3271         import std.format : format;
3272         import std.string : strip;
3273         import std.utf : byCodeUnit;
3274 
3275         auto str = strip(isoExtString);
3276 
3277         enforce!DateTimeException(str.length >= 15,
3278                                   format("Invalid format for DateTime.fromISOExtString: %s", isoExtString));
3279         auto t = str.byCodeUnit.countUntil('T');
3280 
3281         enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromISOExtString: %s", isoExtString));
3282 
3283         immutable date = Date.fromISOExtString(str[0 .. t]);
3284         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3285 
3286         return DateTime(date, tod);
3287     }
3288 
3289     ///
3290     @safe unittest
3291     {
3292         assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
3293                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3294 
3295         assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
3296                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3297 
3298         assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
3299                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3300 
3301         assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
3302                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3303 
3304         assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
3305                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3306     }
3307 
3308     @safe unittest
3309     {
3310         assertThrown!DateTimeException(DateTime.fromISOExtString(""));
3311         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
3312         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
3313         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
3314         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
3315         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
3316 
3317         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
3318         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3319         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3320         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
3321         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
3322         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
3323 
3324         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
3325         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
3326         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
3327         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
3328 
3329         assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
3330         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
3331 
3332         assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3333         assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3334         assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3335         assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3336         assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3337         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3338         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3339     }
3340 
3341     // https://issues.dlang.org/show_bug.cgi?id=17801
3342     @safe unittest
3343     {
3344         import std.conv : to;
3345         import std.meta : AliasSeq;
3346         static foreach (C; AliasSeq!(char, wchar, dchar))
3347         {
3348             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3349                 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3350         }
3351     }
3352 
3353 
3354     /++
3355         Creates a $(LREF DateTime) from a string with the format
3356         YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
3357 
3358         Params:
3359             simpleString = A string formatted in the way that toSimpleString
3360                            formats dates and times.
3361 
3362         Throws:
3363             $(REF DateTimeException,std,datetime,date) if the given string is
3364             not in the correct format or if the resulting $(LREF DateTime)
3365             would not be valid.
3366       +/
3367     static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
3368     if (isSomeString!(S))
3369     {
3370         import std.algorithm.searching : countUntil;
3371         import std.exception : enforce;
3372         import std.format : format;
3373         import std.string : strip;
3374         import std.utf : byCodeUnit;
3375 
3376         auto str = strip(simpleString);
3377 
3378         enforce!DateTimeException(str.length >= 15,
3379                                   format("Invalid format for DateTime.fromSimpleString: %s", simpleString));
3380         auto t = str.byCodeUnit.countUntil(' ');
3381 
3382         enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromSimpleString: %s", simpleString));
3383 
3384         immutable date = Date.fromSimpleString(str[0 .. t]);
3385         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3386 
3387         return DateTime(date, tod);
3388     }
3389 
3390     ///
3391     @safe unittest
3392     {
3393         assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
3394                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3395         assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
3396                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3397         assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
3398                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3399         assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
3400                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3401         assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
3402                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3403     }
3404 
3405     @safe unittest
3406     {
3407         assertThrown!DateTimeException(DateTime.fromISOString(""));
3408         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3409         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3410         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3411         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3412         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3413 
3414         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3415         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3416         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3417         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3418         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3419 
3420         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3421         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3422         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3423         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3424         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3425         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3426 
3427         assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
3428         assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
3429 
3430         assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
3431                DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3432         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
3433                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3434         assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
3435                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3436         assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
3437                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3438         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
3439                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3440         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
3441                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3442         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
3443                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3444     }
3445 
3446     // https://issues.dlang.org/show_bug.cgi?id=17801
3447     @safe unittest
3448     {
3449         import std.conv : to;
3450         import std.meta : AliasSeq;
3451         static foreach (C; AliasSeq!(char, wchar, dchar))
3452         {
3453             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3454                 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3455         }
3456     }
3457 
3458 
3459     /++
3460         Returns the $(LREF DateTime) farthest in the past which is representable
3461         by $(LREF DateTime).
3462       +/
3463     @property static DateTime min() @safe pure nothrow @nogc
3464     out(result)
3465     {
3466         assert(result._date == Date.min);
3467         assert(result._tod == TimeOfDay.min);
3468     }
3469     do
3470     {
3471         auto dt = DateTime.init;
3472         dt._date._year = short.min;
3473         dt._date._month = Month.jan;
3474         dt._date._day = 1;
3475 
3476         return dt;
3477     }
3478 
3479     @safe unittest
3480     {
3481         assert(DateTime.min.year < 0);
3482         assert(DateTime.min < DateTime.max);
3483     }
3484 
3485 
3486     /++
3487         Returns the $(LREF DateTime) farthest in the future which is
3488         representable by $(LREF DateTime).
3489       +/
3490     @property static DateTime max() @safe pure nothrow @nogc
3491     out(result)
3492     {
3493         assert(result._date == Date.max);
3494         assert(result._tod == TimeOfDay.max);
3495     }
3496     do
3497     {
3498         auto dt = DateTime.init;
3499         dt._date._year = short.max;
3500         dt._date._month = Month.dec;
3501         dt._date._day = 31;
3502         dt._tod._hour = TimeOfDay.maxHour;
3503         dt._tod._minute = TimeOfDay.maxMinute;
3504         dt._tod._second = TimeOfDay.maxSecond;
3505 
3506         return dt;
3507     }
3508 
3509     @safe unittest
3510     {
3511         assert(DateTime.max.year > 0);
3512         assert(DateTime.max > DateTime.min);
3513     }
3514 
3515 
3516 private:
3517 
3518     /+
3519         Add seconds to the time of day. Negative values will subtract. If the
3520         number of seconds overflows (or underflows), then the seconds will wrap,
3521         increasing (or decreasing) the number of minutes accordingly. The
3522         same goes for any larger units.
3523 
3524         Params:
3525             seconds = The number of seconds to add to this $(LREF DateTime).
3526       +/
3527     ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
3528     {
3529         import core.time : convert;
3530         long hnsecs = convert!("seconds", "hnsecs")(seconds);
3531         hnsecs += convert!("hours", "hnsecs")(_tod._hour);
3532         hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
3533         hnsecs += convert!("seconds", "hnsecs")(_tod._second);
3534 
3535         auto days = splitUnitsFromHNSecs!"days"(hnsecs);
3536 
3537         if (hnsecs < 0)
3538         {
3539             hnsecs += convert!("days", "hnsecs")(1);
3540             --days;
3541         }
3542 
3543         _date._addDays(days);
3544 
3545         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
3546         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
3547         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
3548 
3549         _tod._hour = cast(ubyte) newHours;
3550         _tod._minute = cast(ubyte) newMinutes;
3551         _tod._second = cast(ubyte) newSeconds;
3552 
3553         return this;
3554     }
3555 
3556     @safe unittest
3557     {
3558         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
3559         {
3560             orig._addSeconds(seconds);
3561             assert(orig == expected);
3562         }
3563 
3564         // Test A.D.
3565         testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
3566         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
3567         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
3568         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
3569         testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
3570         testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
3571         testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
3572         testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
3573         testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
3574         testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
3575         testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
3576         testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
3577         testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
3578         testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
3579 
3580         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
3581         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
3582         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
3583         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
3584         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
3585         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
3586         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
3587         testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
3588         testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
3589 
3590         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
3591         testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
3592         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
3593         testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
3594         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
3595         testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
3596         testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
3597         testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
3598         testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
3599         testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
3600         testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
3601         testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
3602         testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
3603 
3604         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
3605         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
3606         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
3607         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
3608         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
3609         testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
3610 
3611         testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
3612         testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
3613         testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
3614 
3615         testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
3616         testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
3617         testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
3618 
3619         testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
3620         testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
3621         testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
3622 
3623         testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
3624         testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
3625         testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
3626 
3627         testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
3628         testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
3629         testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
3630 
3631         testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
3632         testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
3633         testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
3634 
3635         // Test B.C.
3636         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
3637         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
3638         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
3639         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
3640         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
3641         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
3642         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
3643         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
3644         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
3645         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
3646         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
3647         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
3648         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
3649         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
3650 
3651         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
3652         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
3653         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
3654         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
3655         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
3656         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
3657         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
3658         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
3659         testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
3660 
3661         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
3662         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
3663         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
3664         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
3665         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
3666         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
3667         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
3668         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
3669         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
3670         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
3671         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
3672         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
3673         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
3674 
3675         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
3676         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
3677         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
3678         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
3679         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
3680         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
3681         testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
3682 
3683         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
3684         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
3685         testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
3686 
3687         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
3688         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
3689         testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
3690 
3691         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
3692         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
3693         testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
3694 
3695         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
3696         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
3697         testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
3698 
3699         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
3700         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
3701         testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
3702 
3703         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
3704         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
3705         testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
3706 
3707         // Test Both
3708         testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
3709         testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
3710 
3711         testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
3712         testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
3713 
3714         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
3715         testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
3716 
3717         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
3718         testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
3719 
3720         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3721         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3722         static assert(!__traits(compiles, cdt._addSeconds(4)));
3723         static assert(!__traits(compiles, idt._addSeconds(4)));
3724     }
3725 
3726 
3727     Date      _date;
3728     TimeOfDay _tod;
3729 }
3730 
3731 ///
3732 @safe pure unittest
3733 {
3734     import core.time : days, seconds;
3735 
3736     auto dt = DateTime(2000, 6, 1, 10, 30, 0);
3737 
3738     assert(dt.date == Date(2000, 6, 1));
3739     assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
3740     assert(dt.dayOfYear == 153);
3741     assert(dt.dayOfWeek == DayOfWeek.thu);
3742 
3743     dt += 10.days + 100.seconds;
3744     assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
3745 
3746     assert(dt.toISOExtString() == "2000-06-11T10:31:40");
3747     assert(dt.toISOString() == "20000611T103140");
3748     assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
3749 
3750     assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3751     assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
3752     assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3753 }
3754 
3755 /++
3756     Represents a date in the
3757     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
3758     Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
3759     are A.D. Non-positive years are B.C.
3760 
3761     Year, month, and day are kept separately internally so that `Date` is
3762     optimized for calendar-based operations.
3763 
3764     `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
3765     leap year calculations for its entire length. As per
3766     $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
3767     year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
3768     as a positive integer with 1 B.C. being the year prior to 1 A.D.
3769 
3770     Year 0 is a leap year.
3771  +/
3772 struct Date
3773 {
3774 public:
3775 
3776     /++
3777         Throws:
3778             $(REF DateTimeException,std,datetime,date) if the resulting
3779             $(LREF Date) would not be valid.
3780 
3781         Params:
3782             year  = Year of the Gregorian Calendar. Positive values are A.D.
3783                     Non-positive values are B.C. with year 0 being the year
3784                     prior to 1 A.D.
3785             month = Month of the year (January is 1).
3786             day   = Day of the month.
3787      +/
3788     this(int year, int month, int day) @safe pure
3789     {
3790         enforceValid!"months"(cast(Month) month);
3791         enforceValid!"days"(year, cast(Month) month, day);
3792 
3793         _year  = year.castToYear;
3794         _month = cast(Month) month;
3795         _day   = cast(ubyte) day;
3796     }
3797 
3798     @safe unittest
3799     {
3800         import std.exception : assertNotThrown;
3801         assert(Date(1, 1, 1) == Date.init);
3802 
3803         static void testDate(Date date, int year, int month, int day)
3804         {
3805             assert(date._year == year);
3806             assert(date._month == month);
3807             assert(date._day == day);
3808         }
3809 
3810         testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
3811         testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
3812         testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
3813 
3814         // Test A.D.
3815         assertThrown!DateTimeException(Date(1, 0, 1));
3816         assertThrown!DateTimeException(Date(1, 1, 0));
3817         assertThrown!DateTimeException(Date(1999, 13, 1));
3818         assertThrown!DateTimeException(Date(1999, 1, 32));
3819         assertThrown!DateTimeException(Date(1999, 2, 29));
3820         assertThrown!DateTimeException(Date(2000, 2, 30));
3821         assertThrown!DateTimeException(Date(1999, 3, 32));
3822         assertThrown!DateTimeException(Date(1999, 4, 31));
3823         assertThrown!DateTimeException(Date(1999, 5, 32));
3824         assertThrown!DateTimeException(Date(1999, 6, 31));
3825         assertThrown!DateTimeException(Date(1999, 7, 32));
3826         assertThrown!DateTimeException(Date(1999, 8, 32));
3827         assertThrown!DateTimeException(Date(1999, 9, 31));
3828         assertThrown!DateTimeException(Date(1999, 10, 32));
3829         assertThrown!DateTimeException(Date(1999, 11, 31));
3830         assertThrown!DateTimeException(Date(1999, 12, 32));
3831         assertThrown!DateTimeException(Date(short.max+1, 1, 1));
3832 
3833         assertNotThrown!DateTimeException(Date(1999, 1, 31));
3834         assertNotThrown!DateTimeException(Date(1999, 2, 28));
3835         assertNotThrown!DateTimeException(Date(2000, 2, 29));
3836         assertNotThrown!DateTimeException(Date(1999, 3, 31));
3837         assertNotThrown!DateTimeException(Date(1999, 4, 30));
3838         assertNotThrown!DateTimeException(Date(1999, 5, 31));
3839         assertNotThrown!DateTimeException(Date(1999, 6, 30));
3840         assertNotThrown!DateTimeException(Date(1999, 7, 31));
3841         assertNotThrown!DateTimeException(Date(1999, 8, 31));
3842         assertNotThrown!DateTimeException(Date(1999, 9, 30));
3843         assertNotThrown!DateTimeException(Date(1999, 10, 31));
3844         assertNotThrown!DateTimeException(Date(1999, 11, 30));
3845         assertNotThrown!DateTimeException(Date(1999, 12, 31));
3846 
3847         // Test B.C.
3848         assertNotThrown!DateTimeException(Date(0, 1, 1));
3849         assertNotThrown!DateTimeException(Date(-1, 1, 1));
3850         assertNotThrown!DateTimeException(Date(-1, 12, 31));
3851         assertNotThrown!DateTimeException(Date(-1, 2, 28));
3852         assertNotThrown!DateTimeException(Date(-4, 2, 29));
3853 
3854         assertThrown!DateTimeException(Date(-1, 2, 29));
3855         assertThrown!DateTimeException(Date(-2, 2, 29));
3856         assertThrown!DateTimeException(Date(-3, 2, 29));
3857         assertThrown!DateTimeException(Date(short.min-1, 1, 1));
3858     }
3859 
3860 
3861     /++
3862         Params:
3863             day = The Xth day of the Gregorian Calendar that the constructed
3864                   $(LREF Date) will be for.
3865      +/
3866     this(int day) @safe pure nothrow @nogc
3867     {
3868         if (day > 0)
3869         {
3870             int years = (day / daysIn400Years) * 400 + 1;
3871             day %= daysIn400Years;
3872 
3873             {
3874                 immutable tempYears = day / daysIn100Years;
3875 
3876                 if (tempYears == 4)
3877                 {
3878                     years += 300;
3879                     day -= daysIn100Years * 3;
3880                 }
3881                 else
3882                 {
3883                     years += tempYears * 100;
3884                     day %= daysIn100Years;
3885                 }
3886             }
3887 
3888             years += (day / daysIn4Years) * 4;
3889             day %= daysIn4Years;
3890 
3891             {
3892                 immutable tempYears = day / daysInYear;
3893 
3894                 if (tempYears == 4)
3895                 {
3896                     years += 3;
3897                     day -= daysInYear * 3;
3898                 }
3899                 else
3900                 {
3901                     years += tempYears;
3902                     day %= daysInYear;
3903                 }
3904             }
3905 
3906             if (day == 0)
3907             {
3908                 _year = cast(short)(years - 1);
3909                 _month = Month.dec;
3910                 _day = 31;
3911             }
3912             else
3913             {
3914                 _year = cast(short) years;
3915 
3916                 setDayOfYear(day);
3917             }
3918         }
3919         else if (day <= 0 && -day < daysInLeapYear)
3920         {
3921             _year = 0;
3922 
3923             setDayOfYear(daysInLeapYear + day);
3924         }
3925         else
3926         {
3927             day += daysInLeapYear - 1;
3928             int years = (day / daysIn400Years) * 400 - 1;
3929             day %= daysIn400Years;
3930 
3931             {
3932                 immutable tempYears = day / daysIn100Years;
3933 
3934                 if (tempYears == -4)
3935                 {
3936                     years -= 300;
3937                     day += daysIn100Years * 3;
3938                 }
3939                 else
3940                 {
3941                     years += tempYears * 100;
3942                     day %= daysIn100Years;
3943                 }
3944             }
3945 
3946             years += (day / daysIn4Years) * 4;
3947             day %= daysIn4Years;
3948 
3949             {
3950                 immutable tempYears = day / daysInYear;
3951 
3952                 if (tempYears == -4)
3953                 {
3954                     years -= 3;
3955                     day += daysInYear * 3;
3956                 }
3957                 else
3958                 {
3959                     years += tempYears;
3960                     day %= daysInYear;
3961                 }
3962             }
3963 
3964             if (day == 0)
3965             {
3966                 _year = cast(short)(years + 1);
3967                 _month = Month.jan;
3968                 _day = 1;
3969             }
3970             else
3971             {
3972                 _year = cast(short) years;
3973                 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
3974 
3975                 setDayOfYear(newDoY);
3976             }
3977         }
3978     }
3979 
3980     @safe unittest
3981     {
3982         import std.range : chain;
3983 
3984         // Test A.D.
3985         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
3986             assert(Date(gd.day) == gd.date);
3987     }
3988 
3989 
3990     /++
3991         Compares this $(LREF Date) with the given $(LREF Date).
3992 
3993         Returns:
3994             $(BOOKTABLE,
3995             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
3996             $(TR $(TD this == rhs) $(TD 0))
3997             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
3998             )
3999      +/
4000     int opCmp(Date rhs) const @safe pure nothrow @nogc
4001     {
4002         if (_year < rhs._year)
4003             return -1;
4004         if (_year > rhs._year)
4005             return 1;
4006 
4007         if (_month < rhs._month)
4008             return -1;
4009         if (_month > rhs._month)
4010             return 1;
4011 
4012         if (_day < rhs._day)
4013             return -1;
4014         if (_day > rhs._day)
4015             return 1;
4016 
4017         return 0;
4018     }
4019 
4020     @safe unittest
4021     {
4022         // Test A.D.
4023         assert(Date(1, 1, 1).opCmp(Date.init) == 0);
4024 
4025         assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
4026         assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
4027         assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
4028 
4029         assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
4030         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
4031 
4032         assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
4033 
4034         assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
4035         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
4036         assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
4037         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
4038         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
4039         assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
4040 
4041         assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
4042         assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4043         assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
4044         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
4045         assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
4046         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4047 
4048         // Test B.C.
4049         assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
4050         assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
4051         assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
4052         assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
4053 
4054         assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
4055         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
4056 
4057         assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
4058 
4059         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
4060         assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
4061         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
4062         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
4063         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4064         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
4065 
4066         assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
4067         assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
4068         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4069         assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
4070         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
4071         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
4072 
4073         // Test Both
4074         assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
4075         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
4076 
4077         assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
4078         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
4079 
4080         assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
4081         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
4082 
4083         assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
4084         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
4085 
4086         assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
4087         assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
4088 
4089         auto date = Date(1999, 7, 6);
4090         const cdate = Date(1999, 7, 6);
4091         immutable idate = Date(1999, 7, 6);
4092         assert(date.opCmp(date) == 0);
4093         assert(date.opCmp(cdate) == 0);
4094         assert(date.opCmp(idate) == 0);
4095         assert(cdate.opCmp(date) == 0);
4096         assert(cdate.opCmp(cdate) == 0);
4097         assert(cdate.opCmp(idate) == 0);
4098         assert(idate.opCmp(date) == 0);
4099         assert(idate.opCmp(cdate) == 0);
4100         assert(idate.opCmp(idate) == 0);
4101     }
4102 
4103 
4104     /++
4105         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4106         are B.C.
4107      +/
4108     @property short year() const @safe pure nothrow @nogc
4109     {
4110         return _year;
4111     }
4112 
4113     ///
4114     @safe unittest
4115     {
4116         assert(Date(1999, 7, 6).year == 1999);
4117         assert(Date(2010, 10, 4).year == 2010);
4118         assert(Date(-7, 4, 5).year == -7);
4119     }
4120 
4121     @safe unittest
4122     {
4123         assert(Date.init.year == 1);
4124         assert(Date(1999, 7, 6).year == 1999);
4125         assert(Date(-1999, 7, 6).year == -1999);
4126 
4127         const cdate = Date(1999, 7, 6);
4128         immutable idate = Date(1999, 7, 6);
4129         assert(cdate.year == 1999);
4130         assert(idate.year == 1999);
4131     }
4132 
4133     /++
4134         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4135         are B.C.
4136 
4137         Params:
4138             year = The year to set this Date's year to.
4139 
4140         Throws:
4141             $(REF DateTimeException,std,datetime,date) if the new year is not
4142             a leap year and the resulting date would be on February 29th.
4143      +/
4144     @property void year(int year) @safe pure
4145     {
4146         enforceValid!"days"(year, _month, _day);
4147         _year = year.castToYear;
4148     }
4149 
4150     ///
4151     @safe unittest
4152     {
4153         assert(Date(1999, 7, 6).year == 1999);
4154         assert(Date(2010, 10, 4).year == 2010);
4155         assert(Date(-7, 4, 5).year == -7);
4156     }
4157 
4158     @safe unittest
4159     {
4160         static void testDateInvalid(Date date, int year)
4161         {
4162             date.year = year;
4163         }
4164 
4165         static void testDate(Date date, int year, Date expected)
4166         {
4167             date.year = year;
4168             assert(date == expected);
4169         }
4170 
4171         assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
4172 
4173         testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
4174         testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
4175         testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
4176 
4177         const cdate = Date(1999, 7, 6);
4178         immutable idate = Date(1999, 7, 6);
4179         static assert(!__traits(compiles, cdate.year = 1999));
4180         static assert(!__traits(compiles, idate.year = 1999));
4181     }
4182 
4183 
4184     /++
4185         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4186 
4187         Throws:
4188             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
4189      +/
4190     @property ushort yearBC() const @safe pure
4191     {
4192         import std.format : format;
4193 
4194         if (isAD)
4195             throw new DateTimeException(format("Year %s is A.D.", _year));
4196         return cast(ushort)((_year * -1) + 1);
4197     }
4198 
4199     ///
4200     @safe unittest
4201     {
4202         assert(Date(0, 1, 1).yearBC == 1);
4203         assert(Date(-1, 1, 1).yearBC == 2);
4204         assert(Date(-100, 1, 1).yearBC == 101);
4205     }
4206 
4207     @safe unittest
4208     {
4209         assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
4210 
4211         auto date = Date(0, 7, 6);
4212         const cdate = Date(0, 7, 6);
4213         immutable idate = Date(0, 7, 6);
4214         assert(date.yearBC == 1);
4215         assert(cdate.yearBC == 1);
4216         assert(idate.yearBC == 1);
4217     }
4218 
4219 
4220     /++
4221         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4222 
4223         Params:
4224             year = The year B.C. to set this $(LREF Date)'s year to.
4225 
4226         Throws:
4227             $(REF DateTimeException,std,datetime,date) if a non-positive value
4228             is given.
4229      +/
4230     @property void yearBC(int year) @safe pure
4231     {
4232         if (year <= 0)
4233             throw new DateTimeException("The given year is not a year B.C.");
4234         _year = castToYear((year - 1) * -1);
4235     }
4236 
4237     ///
4238     @safe unittest
4239     {
4240         auto date = Date(2010, 1, 1);
4241         date.yearBC = 1;
4242         assert(date == Date(0, 1, 1));
4243 
4244         date.yearBC = 10;
4245         assert(date == Date(-9, 1, 1));
4246     }
4247 
4248     @safe unittest
4249     {
4250         assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
4251 
4252         auto date = Date(0, 7, 6);
4253         const cdate = Date(0, 7, 6);
4254         immutable idate = Date(0, 7, 6);
4255         date.yearBC = 7;
4256         assert(date.yearBC == 7);
4257         static assert(!__traits(compiles, cdate.yearBC = 7));
4258         static assert(!__traits(compiles, idate.yearBC = 7));
4259     }
4260 
4261 
4262     /++
4263         Month of a Gregorian Year.
4264      +/
4265     @property Month month() const @safe pure nothrow @nogc
4266     {
4267         return _month;
4268     }
4269 
4270     ///
4271     @safe unittest
4272     {
4273         assert(Date(1999, 7, 6).month == 7);
4274         assert(Date(2010, 10, 4).month == 10);
4275         assert(Date(-7, 4, 5).month == 4);
4276     }
4277 
4278     @safe unittest
4279     {
4280         assert(Date.init.month == 1);
4281         assert(Date(1999, 7, 6).month == 7);
4282         assert(Date(-1999, 7, 6).month == 7);
4283 
4284         const cdate = Date(1999, 7, 6);
4285         immutable idate = Date(1999, 7, 6);
4286         assert(cdate.month == 7);
4287         assert(idate.month == 7);
4288     }
4289 
4290     /++
4291         Month of a Gregorian Year.
4292 
4293         Params:
4294             month = The month to set this $(LREF Date)'s month to.
4295 
4296         Throws:
4297             $(REF DateTimeException,std,datetime,date) if the given month is
4298             not a valid month or if the current day would not be valid in the
4299             given month.
4300      +/
4301     @property void month(Month month) @safe pure
4302     {
4303         enforceValid!"months"(month);
4304         enforceValid!"days"(_year, month, _day);
4305         _month = cast(Month) month;
4306     }
4307 
4308     @safe unittest
4309     {
4310         static void testDate(Date date, Month month, Date expected = Date.init)
4311         {
4312             date.month = month;
4313             assert(expected != Date.init);
4314             assert(date == expected);
4315         }
4316 
4317         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
4318         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
4319         assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
4320         assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
4321 
4322         testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
4323         testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
4324 
4325         const cdate = Date(1999, 7, 6);
4326         immutable idate = Date(1999, 7, 6);
4327         static assert(!__traits(compiles, cdate.month = 7));
4328         static assert(!__traits(compiles, idate.month = 7));
4329     }
4330 
4331 
4332     /++
4333         Day of a Gregorian Month.
4334      +/
4335     @property ubyte day() const @safe pure nothrow @nogc
4336     {
4337         return _day;
4338     }
4339 
4340     ///
4341     @safe unittest
4342     {
4343         assert(Date(1999, 7, 6).day == 6);
4344         assert(Date(2010, 10, 4).day == 4);
4345         assert(Date(-7, 4, 5).day == 5);
4346     }
4347 
4348     @safe unittest
4349     {
4350         import std.format : format;
4351         import std.range : chain;
4352 
4353         static void test(Date date, int expected)
4354         {
4355             assert(date.day == expected, format("Value given: %s", date));
4356         }
4357 
4358         foreach (year; chain(testYearsBC, testYearsAD))
4359         {
4360             foreach (md; testMonthDays)
4361                 test(Date(year, md.month, md.day), md.day);
4362         }
4363 
4364         const cdate = Date(1999, 7, 6);
4365         immutable idate = Date(1999, 7, 6);
4366         assert(cdate.day == 6);
4367         assert(idate.day == 6);
4368     }
4369 
4370     /++
4371         Day of a Gregorian Month.
4372 
4373         Params:
4374             day = The day of the month to set this $(LREF Date)'s day to.
4375 
4376         Throws:
4377             $(REF DateTimeException,std,datetime,date) if the given day is not
4378             a valid day of the current month.
4379      +/
4380     @property void day(int day) @safe pure
4381     {
4382         enforceValid!"days"(_year, _month, day);
4383         _day = cast(ubyte) day;
4384     }
4385 
4386     @safe unittest
4387     {
4388         import std.exception : assertNotThrown;
4389 
4390         static void testDate(Date date, int day)
4391         {
4392             date.day = day;
4393         }
4394 
4395         // Test A.D.
4396         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
4397         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
4398         assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
4399         assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
4400         assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
4401         assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
4402         assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
4403         assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
4404         assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
4405         assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
4406         assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
4407         assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
4408         assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
4409         assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
4410 
4411         assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
4412         assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
4413         assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
4414         assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
4415         assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
4416         assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
4417         assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
4418         assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
4419         assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
4420         assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
4421         assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
4422         assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
4423         assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
4424 
4425         {
4426             auto date = Date(1, 1, 1);
4427             date.day = 6;
4428             assert(date == Date(1, 1, 6));
4429         }
4430 
4431         // Test B.C.
4432         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
4433         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
4434         assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
4435         assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
4436         assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
4437         assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
4438         assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
4439         assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
4440         assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
4441         assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
4442         assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
4443         assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
4444         assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
4445         assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
4446 
4447         assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
4448         assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
4449         assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
4450         assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
4451         assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
4452         assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
4453         assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
4454         assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
4455         assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
4456         assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
4457         assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
4458         assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
4459         assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
4460 
4461         {
4462             auto date = Date(-1, 1, 1);
4463             date.day = 6;
4464             assert(date == Date(-1, 1, 6));
4465         }
4466 
4467         const cdate = Date(1999, 7, 6);
4468         immutable idate = Date(1999, 7, 6);
4469         static assert(!__traits(compiles, cdate.day = 6));
4470         static assert(!__traits(compiles, idate.day = 6));
4471     }
4472 
4473 
4474     /++
4475         Adds the given number of years or months to this $(LREF Date), mutating
4476         it. A negative number will subtract.
4477 
4478         Note that if day overflow is allowed, and the date with the adjusted
4479         year/month overflows the number of days in the new month, then the month
4480         will be incremented by one, and the day set to the number of days
4481         overflowed. (e.g. if the day were 31 and the new month were June, then
4482         the month would be incremented to July, and the new day would be 1). If
4483         day overflow is not allowed, then the day will be set to the last valid
4484         day in the month (e.g. June 31st would become June 30th).
4485 
4486         Params:
4487             units         = The type of units to add ("years" or "months").
4488             value         = The number of months or years to add to this
4489                             $(LREF Date).
4490             allowOverflow = Whether the day should be allowed to overflow,
4491                             causing the month to increment.
4492 
4493         Returns:
4494             A reference to the `Date` (`this`).
4495       +/
4496     @safe pure nothrow @nogc
4497     ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4498     if (units == "years")
4499     {
4500         _year += value;
4501 
4502         if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
4503         {
4504             if (allowOverflow == AllowDayOverflow.yes)
4505             {
4506                 _month = Month.mar;
4507                 _day = 1;
4508             }
4509             else
4510                 _day = 28;
4511         }
4512 
4513         return this;
4514     }
4515 
4516     ///
4517     @safe unittest
4518     {
4519         auto d1 = Date(2010, 1, 1);
4520         d1.add!"months"(11);
4521         assert(d1 == Date(2010, 12, 1));
4522 
4523         auto d2 = Date(2010, 1, 1);
4524         d2.add!"months"(-11);
4525         assert(d2 == Date(2009, 2, 1));
4526 
4527         auto d3 = Date(2000, 2, 29);
4528         d3.add!"years"(1);
4529         assert(d3 == Date(2001, 3, 1));
4530 
4531         auto d4 = Date(2000, 2, 29);
4532         d4.add!"years"(1, AllowDayOverflow.no);
4533         assert(d4 == Date(2001, 2, 28));
4534     }
4535 
4536     // Test add!"years"() with AllowDayOverflow.yes
4537     @safe unittest
4538     {
4539         // Test A.D.
4540         {
4541             auto date = Date(1999, 7, 6);
4542             date.add!"years"(7);
4543             assert(date == Date(2006, 7, 6));
4544             date.add!"years"(-9);
4545             assert(date == Date(1997, 7, 6));
4546         }
4547 
4548         {
4549             auto date = Date(1999, 2, 28);
4550             date.add!"years"(1);
4551             assert(date == Date(2000, 2, 28));
4552         }
4553 
4554         {
4555             auto date = Date(2000, 2, 29);
4556             date.add!"years"(-1);
4557             assert(date == Date(1999, 3, 1));
4558         }
4559 
4560         // Test B.C.
4561         {
4562             auto date = Date(-1999, 7, 6);
4563             date.add!"years"(-7);
4564             assert(date == Date(-2006, 7, 6));
4565             date.add!"years"(9);
4566             assert(date == Date(-1997, 7, 6));
4567         }
4568 
4569         {
4570             auto date = Date(-1999, 2, 28);
4571             date.add!"years"(-1);
4572             assert(date == Date(-2000, 2, 28));
4573         }
4574 
4575         {
4576             auto date = Date(-2000, 2, 29);
4577             date.add!"years"(1);
4578             assert(date == Date(-1999, 3, 1));
4579         }
4580 
4581         // Test Both
4582         {
4583             auto date = Date(4, 7, 6);
4584             date.add!"years"(-5);
4585             assert(date == Date(-1, 7, 6));
4586             date.add!"years"(5);
4587             assert(date == Date(4, 7, 6));
4588         }
4589 
4590         {
4591             auto date = Date(-4, 7, 6);
4592             date.add!"years"(5);
4593             assert(date == Date(1, 7, 6));
4594             date.add!"years"(-5);
4595             assert(date == Date(-4, 7, 6));
4596         }
4597 
4598         {
4599             auto date = Date(4, 7, 6);
4600             date.add!"years"(-8);
4601             assert(date == Date(-4, 7, 6));
4602             date.add!"years"(8);
4603             assert(date == Date(4, 7, 6));
4604         }
4605 
4606         {
4607             auto date = Date(-4, 7, 6);
4608             date.add!"years"(8);
4609             assert(date == Date(4, 7, 6));
4610             date.add!"years"(-8);
4611             assert(date == Date(-4, 7, 6));
4612         }
4613 
4614         {
4615             auto date = Date(-4, 2, 29);
4616             date.add!"years"(5);
4617             assert(date == Date(1, 3, 1));
4618         }
4619 
4620         {
4621             auto date = Date(4, 2, 29);
4622             date.add!"years"(-5);
4623             assert(date == Date(-1, 3, 1));
4624         }
4625 
4626         {
4627             auto date = Date(4, 2, 29);
4628             date.add!"years"(-5).add!"years"(7);
4629             assert(date == Date(6, 3, 1));
4630         }
4631 
4632         const cdate = Date(1999, 7, 6);
4633         immutable idate = Date(1999, 7, 6);
4634         static assert(!__traits(compiles, cdate.add!"years"(7)));
4635         static assert(!__traits(compiles, idate.add!"years"(7)));
4636     }
4637 
4638     // Test add!"years"() with AllowDayOverflow.no
4639     @safe unittest
4640     {
4641         // Test A.D.
4642         {
4643             auto date = Date(1999, 7, 6);
4644             date.add!"years"(7, AllowDayOverflow.no);
4645             assert(date == Date(2006, 7, 6));
4646             date.add!"years"(-9, AllowDayOverflow.no);
4647             assert(date == Date(1997, 7, 6));
4648         }
4649 
4650         {
4651             auto date = Date(1999, 2, 28);
4652             date.add!"years"(1, AllowDayOverflow.no);
4653             assert(date == Date(2000, 2, 28));
4654         }
4655 
4656         {
4657             auto date = Date(2000, 2, 29);
4658             date.add!"years"(-1, AllowDayOverflow.no);
4659             assert(date == Date(1999, 2, 28));
4660         }
4661 
4662         // Test B.C.
4663         {
4664             auto date = Date(-1999, 7, 6);
4665             date.add!"years"(-7, AllowDayOverflow.no);
4666             assert(date == Date(-2006, 7, 6));
4667             date.add!"years"(9, AllowDayOverflow.no);
4668             assert(date == Date(-1997, 7, 6));
4669         }
4670 
4671         {
4672             auto date = Date(-1999, 2, 28);
4673             date.add!"years"(-1, AllowDayOverflow.no);
4674             assert(date == Date(-2000, 2, 28));
4675         }
4676 
4677         {
4678             auto date = Date(-2000, 2, 29);
4679             date.add!"years"(1, AllowDayOverflow.no);
4680             assert(date == Date(-1999, 2, 28));
4681         }
4682 
4683         // Test Both
4684         {
4685             auto date = Date(4, 7, 6);
4686             date.add!"years"(-5, AllowDayOverflow.no);
4687             assert(date == Date(-1, 7, 6));
4688             date.add!"years"(5, AllowDayOverflow.no);
4689             assert(date == Date(4, 7, 6));
4690         }
4691 
4692         {
4693             auto date = Date(-4, 7, 6);
4694             date.add!"years"(5, AllowDayOverflow.no);
4695             assert(date == Date(1, 7, 6));
4696             date.add!"years"(-5, AllowDayOverflow.no);
4697             assert(date == Date(-4, 7, 6));
4698         }
4699 
4700         {
4701             auto date = Date(4, 7, 6);
4702             date.add!"years"(-8, AllowDayOverflow.no);
4703             assert(date == Date(-4, 7, 6));
4704             date.add!"years"(8, AllowDayOverflow.no);
4705             assert(date == Date(4, 7, 6));
4706         }
4707 
4708         {
4709             auto date = Date(-4, 7, 6);
4710             date.add!"years"(8, AllowDayOverflow.no);
4711             assert(date == Date(4, 7, 6));
4712             date.add!"years"(-8, AllowDayOverflow.no);
4713             assert(date == Date(-4, 7, 6));
4714         }
4715 
4716         {
4717             auto date = Date(-4, 2, 29);
4718             date.add!"years"(5, AllowDayOverflow.no);
4719             assert(date == Date(1, 2, 28));
4720         }
4721 
4722         {
4723             auto date = Date(4, 2, 29);
4724             date.add!"years"(-5, AllowDayOverflow.no);
4725             assert(date == Date(-1, 2, 28));
4726         }
4727 
4728         {
4729             auto date = Date(4, 2, 29);
4730             date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
4731             assert(date == Date(6, 2, 28));
4732         }
4733     }
4734 
4735 
4736     // Shares documentation with "years" version.
4737     @safe pure nothrow @nogc
4738     ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4739     if (units == "months")
4740     {
4741         auto years = months / 12;
4742         months %= 12;
4743         auto newMonth = _month + months;
4744 
4745         if (months < 0)
4746         {
4747             if (newMonth < 1)
4748             {
4749                 newMonth += 12;
4750                 --years;
4751             }
4752         }
4753         else if (newMonth > 12)
4754         {
4755             newMonth -= 12;
4756             ++years;
4757         }
4758 
4759         _year += years;
4760         _month = cast(Month) newMonth;
4761 
4762         immutable currMaxDay = maxDay(_year, _month);
4763         immutable overflow = _day - currMaxDay;
4764 
4765         if (overflow > 0)
4766         {
4767             if (allowOverflow == AllowDayOverflow.yes)
4768             {
4769                 ++_month;
4770                 _day = cast(ubyte) overflow;
4771             }
4772             else
4773                 _day = cast(ubyte) currMaxDay;
4774         }
4775 
4776         return this;
4777     }
4778 
4779     // Test add!"months"() with AllowDayOverflow.yes
4780     @safe unittest
4781     {
4782         // Test A.D.
4783         {
4784             auto date = Date(1999, 7, 6);
4785             date.add!"months"(3);
4786             assert(date == Date(1999, 10, 6));
4787             date.add!"months"(-4);
4788             assert(date == Date(1999, 6, 6));
4789         }
4790 
4791         {
4792             auto date = Date(1999, 7, 6);
4793             date.add!"months"(6);
4794             assert(date == Date(2000, 1, 6));
4795             date.add!"months"(-6);
4796             assert(date == Date(1999, 7, 6));
4797         }
4798 
4799         {
4800             auto date = Date(1999, 7, 6);
4801             date.add!"months"(27);
4802             assert(date == Date(2001, 10, 6));
4803             date.add!"months"(-28);
4804             assert(date == Date(1999, 6, 6));
4805         }
4806 
4807         {
4808             auto date = Date(1999, 5, 31);
4809             date.add!"months"(1);
4810             assert(date == Date(1999, 7, 1));
4811         }
4812 
4813         {
4814             auto date = Date(1999, 5, 31);
4815             date.add!"months"(-1);
4816             assert(date == Date(1999, 5, 1));
4817         }
4818 
4819         {
4820             auto date = Date(1999, 2, 28);
4821             date.add!"months"(12);
4822             assert(date == Date(2000, 2, 28));
4823         }
4824 
4825         {
4826             auto date = Date(2000, 2, 29);
4827             date.add!"months"(12);
4828             assert(date == Date(2001, 3, 1));
4829         }
4830 
4831         {
4832             auto date = Date(1999, 7, 31);
4833             date.add!"months"(1);
4834             assert(date == Date(1999, 8, 31));
4835             date.add!"months"(1);
4836             assert(date == Date(1999, 10, 1));
4837         }
4838 
4839         {
4840             auto date = Date(1998, 8, 31);
4841             date.add!"months"(13);
4842             assert(date == Date(1999, 10, 1));
4843             date.add!"months"(-13);
4844             assert(date == Date(1998, 9, 1));
4845         }
4846 
4847         {
4848             auto date = Date(1997, 12, 31);
4849             date.add!"months"(13);
4850             assert(date == Date(1999, 1, 31));
4851             date.add!"months"(-13);
4852             assert(date == Date(1997, 12, 31));
4853         }
4854 
4855         {
4856             auto date = Date(1997, 12, 31);
4857             date.add!"months"(14);
4858             assert(date == Date(1999, 3, 3));
4859             date.add!"months"(-14);
4860             assert(date == Date(1998, 1, 3));
4861         }
4862 
4863         {
4864             auto date = Date(1998, 12, 31);
4865             date.add!"months"(14);
4866             assert(date == Date(2000, 3, 2));
4867             date.add!"months"(-14);
4868             assert(date == Date(1999, 1, 2));
4869         }
4870 
4871         {
4872             auto date = Date(1999, 12, 31);
4873             date.add!"months"(14);
4874             assert(date == Date(2001, 3, 3));
4875             date.add!"months"(-14);
4876             assert(date == Date(2000, 1, 3));
4877         }
4878 
4879         // Test B.C.
4880         {
4881             auto date = Date(-1999, 7, 6);
4882             date.add!"months"(3);
4883             assert(date == Date(-1999, 10, 6));
4884             date.add!"months"(-4);
4885             assert(date == Date(-1999, 6, 6));
4886         }
4887 
4888         {
4889             auto date = Date(-1999, 7, 6);
4890             date.add!"months"(6);
4891             assert(date == Date(-1998, 1, 6));
4892             date.add!"months"(-6);
4893             assert(date == Date(-1999, 7, 6));
4894         }
4895 
4896         {
4897             auto date = Date(-1999, 7, 6);
4898             date.add!"months"(-27);
4899             assert(date == Date(-2001, 4, 6));
4900             date.add!"months"(28);
4901             assert(date == Date(-1999, 8, 6));
4902         }
4903 
4904         {
4905             auto date = Date(-1999, 5, 31);
4906             date.add!"months"(1);
4907             assert(date == Date(-1999, 7, 1));
4908         }
4909 
4910         {
4911             auto date = Date(-1999, 5, 31);
4912             date.add!"months"(-1);
4913             assert(date == Date(-1999, 5, 1));
4914         }
4915 
4916         {
4917             auto date = Date(-1999, 2, 28);
4918             date.add!"months"(-12);
4919             assert(date == Date(-2000, 2, 28));
4920         }
4921 
4922         {
4923             auto date = Date(-2000, 2, 29);
4924             date.add!"months"(-12);
4925             assert(date == Date(-2001, 3, 1));
4926         }
4927 
4928         {
4929             auto date = Date(-1999, 7, 31);
4930             date.add!"months"(1);
4931             assert(date == Date(-1999, 8, 31));
4932             date.add!"months"(1);
4933             assert(date == Date(-1999, 10, 1));
4934         }
4935 
4936         {
4937             auto date = Date(-1998, 8, 31);
4938             date.add!"months"(13);
4939             assert(date == Date(-1997, 10, 1));
4940             date.add!"months"(-13);
4941             assert(date == Date(-1998, 9, 1));
4942         }
4943 
4944         {
4945             auto date = Date(-1997, 12, 31);
4946             date.add!"months"(13);
4947             assert(date == Date(-1995, 1, 31));
4948             date.add!"months"(-13);
4949             assert(date == Date(-1997, 12, 31));
4950         }
4951 
4952         {
4953             auto date = Date(-1997, 12, 31);
4954             date.add!"months"(14);
4955             assert(date == Date(-1995, 3, 3));
4956             date.add!"months"(-14);
4957             assert(date == Date(-1996, 1, 3));
4958         }
4959 
4960         {
4961             auto date = Date(-2002, 12, 31);
4962             date.add!"months"(14);
4963             assert(date == Date(-2000, 3, 2));
4964             date.add!"months"(-14);
4965             assert(date == Date(-2001, 1, 2));
4966         }
4967 
4968         {
4969             auto date = Date(-2001, 12, 31);
4970             date.add!"months"(14);
4971             assert(date == Date(-1999, 3, 3));
4972             date.add!"months"(-14);
4973             assert(date == Date(-2000, 1, 3));
4974         }
4975 
4976         // Test Both
4977         {
4978             auto date = Date(1, 1, 1);
4979             date.add!"months"(-1);
4980             assert(date == Date(0, 12, 1));
4981             date.add!"months"(1);
4982             assert(date == Date(1, 1, 1));
4983         }
4984 
4985         {
4986             auto date = Date(4, 1, 1);
4987             date.add!"months"(-48);
4988             assert(date == Date(0, 1, 1));
4989             date.add!"months"(48);
4990             assert(date == Date(4, 1, 1));
4991         }
4992 
4993         {
4994             auto date = Date(4, 3, 31);
4995             date.add!"months"(-49);
4996             assert(date == Date(0, 3, 2));
4997             date.add!"months"(49);
4998             assert(date == Date(4, 4, 2));
4999         }
5000 
5001         {
5002             auto date = Date(4, 3, 31);
5003             date.add!"months"(-85);
5004             assert(date == Date(-3, 3, 3));
5005             date.add!"months"(85);
5006             assert(date == Date(4, 4, 3));
5007         }
5008 
5009         {
5010             auto date = Date(-3, 3, 31);
5011             date.add!"months"(85).add!"months"(-83);
5012             assert(date == Date(-3, 6, 1));
5013         }
5014 
5015         const cdate = Date(1999, 7, 6);
5016         immutable idate = Date(1999, 7, 6);
5017         static assert(!__traits(compiles, cdate.add!"months"(3)));
5018         static assert(!__traits(compiles, idate.add!"months"(3)));
5019     }
5020 
5021     // Test add!"months"() with AllowDayOverflow.no
5022     @safe unittest
5023     {
5024         // Test A.D.
5025         {
5026             auto date = Date(1999, 7, 6);
5027             date.add!"months"(3, AllowDayOverflow.no);
5028             assert(date == Date(1999, 10, 6));
5029             date.add!"months"(-4, AllowDayOverflow.no);
5030             assert(date == Date(1999, 6, 6));
5031         }
5032 
5033         {
5034             auto date = Date(1999, 7, 6);
5035             date.add!"months"(6, AllowDayOverflow.no);
5036             assert(date == Date(2000, 1, 6));
5037             date.add!"months"(-6, AllowDayOverflow.no);
5038             assert(date == Date(1999, 7, 6));
5039         }
5040 
5041         {
5042             auto date = Date(1999, 7, 6);
5043             date.add!"months"(27, AllowDayOverflow.no);
5044             assert(date == Date(2001, 10, 6));
5045             date.add!"months"(-28, AllowDayOverflow.no);
5046             assert(date == Date(1999, 6, 6));
5047         }
5048 
5049         {
5050             auto date = Date(1999, 5, 31);
5051             date.add!"months"(1, AllowDayOverflow.no);
5052             assert(date == Date(1999, 6, 30));
5053         }
5054 
5055         {
5056             auto date = Date(1999, 5, 31);
5057             date.add!"months"(-1, AllowDayOverflow.no);
5058             assert(date == Date(1999, 4, 30));
5059         }
5060 
5061         {
5062             auto date = Date(1999, 2, 28);
5063             date.add!"months"(12, AllowDayOverflow.no);
5064             assert(date == Date(2000, 2, 28));
5065         }
5066 
5067         {
5068             auto date = Date(2000, 2, 29);
5069             date.add!"months"(12, AllowDayOverflow.no);
5070             assert(date == Date(2001, 2, 28));
5071         }
5072 
5073         {
5074             auto date = Date(1999, 7, 31);
5075             date.add!"months"(1, AllowDayOverflow.no);
5076             assert(date == Date(1999, 8, 31));
5077             date.add!"months"(1, AllowDayOverflow.no);
5078             assert(date == Date(1999, 9, 30));
5079         }
5080 
5081         {
5082             auto date = Date(1998, 8, 31);
5083             date.add!"months"(13, AllowDayOverflow.no);
5084             assert(date == Date(1999, 9, 30));
5085             date.add!"months"(-13, AllowDayOverflow.no);
5086             assert(date == Date(1998, 8, 30));
5087         }
5088 
5089         {
5090             auto date = Date(1997, 12, 31);
5091             date.add!"months"(13, AllowDayOverflow.no);
5092             assert(date == Date(1999, 1, 31));
5093             date.add!"months"(-13, AllowDayOverflow.no);
5094             assert(date == Date(1997, 12, 31));
5095         }
5096 
5097         {
5098             auto date = Date(1997, 12, 31);
5099             date.add!"months"(14, AllowDayOverflow.no);
5100             assert(date == Date(1999, 2, 28));
5101             date.add!"months"(-14, AllowDayOverflow.no);
5102             assert(date == Date(1997, 12, 28));
5103         }
5104 
5105         {
5106             auto date = Date(1998, 12, 31);
5107             date.add!"months"(14, AllowDayOverflow.no);
5108             assert(date == Date(2000, 2, 29));
5109             date.add!"months"(-14, AllowDayOverflow.no);
5110             assert(date == Date(1998, 12, 29));
5111         }
5112 
5113         {
5114             auto date = Date(1999, 12, 31);
5115             date.add!"months"(14, AllowDayOverflow.no);
5116             assert(date == Date(2001, 2, 28));
5117             date.add!"months"(-14, AllowDayOverflow.no);
5118             assert(date == Date(1999, 12, 28));
5119         }
5120 
5121         // Test B.C.
5122         {
5123             auto date = Date(-1999, 7, 6);
5124             date.add!"months"(3, AllowDayOverflow.no);
5125             assert(date == Date(-1999, 10, 6));
5126             date.add!"months"(-4, AllowDayOverflow.no);
5127             assert(date == Date(-1999, 6, 6));
5128         }
5129 
5130         {
5131             auto date = Date(-1999, 7, 6);
5132             date.add!"months"(6, AllowDayOverflow.no);
5133             assert(date == Date(-1998, 1, 6));
5134             date.add!"months"(-6, AllowDayOverflow.no);
5135             assert(date == Date(-1999, 7, 6));
5136         }
5137 
5138         {
5139             auto date = Date(-1999, 7, 6);
5140             date.add!"months"(-27, AllowDayOverflow.no);
5141             assert(date == Date(-2001, 4, 6));
5142             date.add!"months"(28, AllowDayOverflow.no);
5143             assert(date == Date(-1999, 8, 6));
5144         }
5145 
5146         {
5147             auto date = Date(-1999, 5, 31);
5148             date.add!"months"(1, AllowDayOverflow.no);
5149             assert(date == Date(-1999, 6, 30));
5150         }
5151 
5152         {
5153             auto date = Date(-1999, 5, 31);
5154             date.add!"months"(-1, AllowDayOverflow.no);
5155             assert(date == Date(-1999, 4, 30));
5156         }
5157 
5158         {
5159             auto date = Date(-1999, 2, 28);
5160             date.add!"months"(-12, AllowDayOverflow.no);
5161             assert(date == Date(-2000, 2, 28));
5162         }
5163 
5164         {
5165             auto date = Date(-2000, 2, 29);
5166             date.add!"months"(-12, AllowDayOverflow.no);
5167             assert(date == Date(-2001, 2, 28));
5168         }
5169 
5170         {
5171             auto date = Date(-1999, 7, 31);
5172             date.add!"months"(1, AllowDayOverflow.no);
5173             assert(date == Date(-1999, 8, 31));
5174             date.add!"months"(1, AllowDayOverflow.no);
5175             assert(date == Date(-1999, 9, 30));
5176         }
5177 
5178         {
5179             auto date = Date(-1998, 8, 31);
5180             date.add!"months"(13, AllowDayOverflow.no);
5181             assert(date == Date(-1997, 9, 30));
5182             date.add!"months"(-13, AllowDayOverflow.no);
5183             assert(date == Date(-1998, 8, 30));
5184         }
5185 
5186         {
5187             auto date = Date(-1997, 12, 31);
5188             date.add!"months"(13, AllowDayOverflow.no);
5189             assert(date == Date(-1995, 1, 31));
5190             date.add!"months"(-13, AllowDayOverflow.no);
5191             assert(date == Date(-1997, 12, 31));
5192         }
5193 
5194         {
5195             auto date = Date(-1997, 12, 31);
5196             date.add!"months"(14, AllowDayOverflow.no);
5197             assert(date == Date(-1995, 2, 28));
5198             date.add!"months"(-14, AllowDayOverflow.no);
5199             assert(date == Date(-1997, 12, 28));
5200         }
5201 
5202         {
5203             auto date = Date(-2002, 12, 31);
5204             date.add!"months"(14, AllowDayOverflow.no);
5205             assert(date == Date(-2000, 2, 29));
5206             date.add!"months"(-14, AllowDayOverflow.no);
5207             assert(date == Date(-2002, 12, 29));
5208         }
5209 
5210         {
5211             auto date = Date(-2001, 12, 31);
5212             date.add!"months"(14, AllowDayOverflow.no);
5213             assert(date == Date(-1999, 2, 28));
5214             date.add!"months"(-14, AllowDayOverflow.no);
5215             assert(date == Date(-2001, 12, 28));
5216         }
5217 
5218         // Test Both
5219         {
5220             auto date = Date(1, 1, 1);
5221             date.add!"months"(-1, AllowDayOverflow.no);
5222             assert(date == Date(0, 12, 1));
5223             date.add!"months"(1, AllowDayOverflow.no);
5224             assert(date == Date(1, 1, 1));
5225         }
5226 
5227         {
5228             auto date = Date(4, 1, 1);
5229             date.add!"months"(-48, AllowDayOverflow.no);
5230             assert(date == Date(0, 1, 1));
5231             date.add!"months"(48, AllowDayOverflow.no);
5232             assert(date == Date(4, 1, 1));
5233         }
5234 
5235         {
5236             auto date = Date(4, 3, 31);
5237             date.add!"months"(-49, AllowDayOverflow.no);
5238             assert(date == Date(0, 2, 29));
5239             date.add!"months"(49, AllowDayOverflow.no);
5240             assert(date == Date(4, 3, 29));
5241         }
5242 
5243         {
5244             auto date = Date(4, 3, 31);
5245             date.add!"months"(-85, AllowDayOverflow.no);
5246             assert(date == Date(-3, 2, 28));
5247             date.add!"months"(85, AllowDayOverflow.no);
5248             assert(date == Date(4, 3, 28));
5249         }
5250 
5251         {
5252             auto date = Date(-3, 3, 31);
5253             date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
5254             assert(date == Date(-3, 5, 30));
5255         }
5256     }
5257 
5258 
5259     /++
5260         Adds the given number of years or months to this $(LREF Date), mutating
5261         it. A negative number will subtract.
5262 
5263         The difference between rolling and adding is that rolling does not
5264         affect larger units. Rolling a $(LREF Date) 12 months gets
5265         the exact same $(LREF Date). However, the days can still be affected due
5266         to the differing number of days in each month.
5267 
5268         Because there are no units larger than years, there is no difference
5269         between adding and rolling years.
5270 
5271         Params:
5272             units         = The type of units to add ("years" or "months").
5273             value         = The number of months or years to add to this
5274                             $(LREF Date).
5275             allowOverflow = Whether the day should be allowed to overflow,
5276                             causing the month to increment.
5277 
5278         Returns:
5279             A reference to the `Date` (`this`).
5280       +/
5281     @safe pure nothrow @nogc
5282     ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5283     if (units == "years")
5284     {
5285         return add!"years"(value, allowOverflow);
5286     }
5287 
5288     ///
5289     @safe unittest
5290     {
5291         auto d1 = Date(2010, 1, 1);
5292         d1.roll!"months"(1);
5293         assert(d1 == Date(2010, 2, 1));
5294 
5295         auto d2 = Date(2010, 1, 1);
5296         d2.roll!"months"(-1);
5297         assert(d2 == Date(2010, 12, 1));
5298 
5299         auto d3 = Date(1999, 1, 29);
5300         d3.roll!"months"(1);
5301         assert(d3 == Date(1999, 3, 1));
5302 
5303         auto d4 = Date(1999, 1, 29);
5304         d4.roll!"months"(1, AllowDayOverflow.no);
5305         assert(d4 == Date(1999, 2, 28));
5306 
5307         auto d5 = Date(2000, 2, 29);
5308         d5.roll!"years"(1);
5309         assert(d5 == Date(2001, 3, 1));
5310 
5311         auto d6 = Date(2000, 2, 29);
5312         d6.roll!"years"(1, AllowDayOverflow.no);
5313         assert(d6 == Date(2001, 2, 28));
5314     }
5315 
5316     @safe unittest
5317     {
5318         const cdate = Date(1999, 7, 6);
5319         immutable idate = Date(1999, 7, 6);
5320         static assert(!__traits(compiles, cdate.roll!"years"(3)));
5321         static assert(!__traits(compiles, idate.rolYears(3)));
5322     }
5323 
5324 
5325     // Shares documentation with "years" version.
5326     @safe pure nothrow @nogc
5327     ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5328     if (units == "months")
5329     {
5330         months %= 12;
5331         auto newMonth = _month + months;
5332 
5333         if (months < 0)
5334         {
5335             if (newMonth < 1)
5336                 newMonth += 12;
5337         }
5338         else
5339         {
5340             if (newMonth > 12)
5341                 newMonth -= 12;
5342         }
5343 
5344         _month = cast(Month) newMonth;
5345 
5346         immutable currMaxDay = maxDay(_year, _month);
5347         immutable overflow = _day - currMaxDay;
5348 
5349         if (overflow > 0)
5350         {
5351             if (allowOverflow == AllowDayOverflow.yes)
5352             {
5353                 ++_month;
5354                 _day = cast(ubyte) overflow;
5355             }
5356             else
5357                 _day = cast(ubyte) currMaxDay;
5358         }
5359 
5360         return this;
5361     }
5362 
5363     // Test roll!"months"() with AllowDayOverflow.yes
5364     @safe unittest
5365     {
5366         // Test A.D.
5367         {
5368             auto date = Date(1999, 7, 6);
5369             date.roll!"months"(3);
5370             assert(date == Date(1999, 10, 6));
5371             date.roll!"months"(-4);
5372             assert(date == Date(1999, 6, 6));
5373         }
5374 
5375         {
5376             auto date = Date(1999, 7, 6);
5377             date.roll!"months"(6);
5378             assert(date == Date(1999, 1, 6));
5379             date.roll!"months"(-6);
5380             assert(date == Date(1999, 7, 6));
5381         }
5382 
5383         {
5384             auto date = Date(1999, 7, 6);
5385             date.roll!"months"(27);
5386             assert(date == Date(1999, 10, 6));
5387             date.roll!"months"(-28);
5388             assert(date == Date(1999, 6, 6));
5389         }
5390 
5391         {
5392             auto date = Date(1999, 5, 31);
5393             date.roll!"months"(1);
5394             assert(date == Date(1999, 7, 1));
5395         }
5396 
5397         {
5398             auto date = Date(1999, 5, 31);
5399             date.roll!"months"(-1);
5400             assert(date == Date(1999, 5, 1));
5401         }
5402 
5403         {
5404             auto date = Date(1999, 2, 28);
5405             date.roll!"months"(12);
5406             assert(date == Date(1999, 2, 28));
5407         }
5408 
5409         {
5410             auto date = Date(2000, 2, 29);
5411             date.roll!"months"(12);
5412             assert(date == Date(2000, 2, 29));
5413         }
5414 
5415         {
5416             auto date = Date(1999, 7, 31);
5417             date.roll!"months"(1);
5418             assert(date == Date(1999, 8, 31));
5419             date.roll!"months"(1);
5420             assert(date == Date(1999, 10, 1));
5421         }
5422 
5423         {
5424             auto date = Date(1998, 8, 31);
5425             date.roll!"months"(13);
5426             assert(date == Date(1998, 10, 1));
5427             date.roll!"months"(-13);
5428             assert(date == Date(1998, 9, 1));
5429         }
5430 
5431         {
5432             auto date = Date(1997, 12, 31);
5433             date.roll!"months"(13);
5434             assert(date == Date(1997, 1, 31));
5435             date.roll!"months"(-13);
5436             assert(date == Date(1997, 12, 31));
5437         }
5438 
5439         {
5440             auto date = Date(1997, 12, 31);
5441             date.roll!"months"(14);
5442             assert(date == Date(1997, 3, 3));
5443             date.roll!"months"(-14);
5444             assert(date == Date(1997, 1, 3));
5445         }
5446 
5447         {
5448             auto date = Date(1998, 12, 31);
5449             date.roll!"months"(14);
5450             assert(date == Date(1998, 3, 3));
5451             date.roll!"months"(-14);
5452             assert(date == Date(1998, 1, 3));
5453         }
5454 
5455         {
5456             auto date = Date(1999, 12, 31);
5457             date.roll!"months"(14);
5458             assert(date == Date(1999, 3, 3));
5459             date.roll!"months"(-14);
5460             assert(date == Date(1999, 1, 3));
5461         }
5462 
5463         // Test B.C.
5464         {
5465             auto date = Date(-1999, 7, 6);
5466             date.roll!"months"(3);
5467             assert(date == Date(-1999, 10, 6));
5468             date.roll!"months"(-4);
5469             assert(date == Date(-1999, 6, 6));
5470         }
5471 
5472         {
5473             auto date = Date(-1999, 7, 6);
5474             date.roll!"months"(6);
5475             assert(date == Date(-1999, 1, 6));
5476             date.roll!"months"(-6);
5477             assert(date == Date(-1999, 7, 6));
5478         }
5479 
5480         {
5481             auto date = Date(-1999, 7, 6);
5482             date.roll!"months"(-27);
5483             assert(date == Date(-1999, 4, 6));
5484             date.roll!"months"(28);
5485             assert(date == Date(-1999, 8, 6));
5486         }
5487 
5488         {
5489             auto date = Date(-1999, 5, 31);
5490             date.roll!"months"(1);
5491             assert(date == Date(-1999, 7, 1));
5492         }
5493 
5494         {
5495             auto date = Date(-1999, 5, 31);
5496             date.roll!"months"(-1);
5497             assert(date == Date(-1999, 5, 1));
5498         }
5499 
5500         {
5501             auto date = Date(-1999, 2, 28);
5502             date.roll!"months"(-12);
5503             assert(date == Date(-1999, 2, 28));
5504         }
5505 
5506         {
5507             auto date = Date(-2000, 2, 29);
5508             date.roll!"months"(-12);
5509             assert(date == Date(-2000, 2, 29));
5510         }
5511 
5512         {
5513             auto date = Date(-1999, 7, 31);
5514             date.roll!"months"(1);
5515             assert(date == Date(-1999, 8, 31));
5516             date.roll!"months"(1);
5517             assert(date == Date(-1999, 10, 1));
5518         }
5519 
5520         {
5521             auto date = Date(-1998, 8, 31);
5522             date.roll!"months"(13);
5523             assert(date == Date(-1998, 10, 1));
5524             date.roll!"months"(-13);
5525             assert(date == Date(-1998, 9, 1));
5526         }
5527 
5528         {
5529             auto date = Date(-1997, 12, 31);
5530             date.roll!"months"(13);
5531             assert(date == Date(-1997, 1, 31));
5532             date.roll!"months"(-13);
5533             assert(date == Date(-1997, 12, 31));
5534         }
5535 
5536         {
5537             auto date = Date(-1997, 12, 31);
5538             date.roll!"months"(14);
5539             assert(date == Date(-1997, 3, 3));
5540             date.roll!"months"(-14);
5541             assert(date == Date(-1997, 1, 3));
5542         }
5543 
5544         {
5545             auto date = Date(-2002, 12, 31);
5546             date.roll!"months"(14);
5547             assert(date == Date(-2002, 3, 3));
5548             date.roll!"months"(-14);
5549             assert(date == Date(-2002, 1, 3));
5550         }
5551 
5552         {
5553             auto date = Date(-2001, 12, 31);
5554             date.roll!"months"(14);
5555             assert(date == Date(-2001, 3, 3));
5556             date.roll!"months"(-14);
5557             assert(date == Date(-2001, 1, 3));
5558         }
5559 
5560         // Test Both
5561         {
5562             auto date = Date(1, 1, 1);
5563             date.roll!"months"(-1);
5564             assert(date == Date(1, 12, 1));
5565             date.roll!"months"(1);
5566             assert(date == Date(1, 1, 1));
5567         }
5568 
5569         {
5570             auto date = Date(4, 1, 1);
5571             date.roll!"months"(-48);
5572             assert(date == Date(4, 1, 1));
5573             date.roll!"months"(48);
5574             assert(date == Date(4, 1, 1));
5575         }
5576 
5577         {
5578             auto date = Date(4, 3, 31);
5579             date.roll!"months"(-49);
5580             assert(date == Date(4, 3, 2));
5581             date.roll!"months"(49);
5582             assert(date == Date(4, 4, 2));
5583         }
5584 
5585         {
5586             auto date = Date(4, 3, 31);
5587             date.roll!"months"(-85);
5588             assert(date == Date(4, 3, 2));
5589             date.roll!"months"(85);
5590             assert(date == Date(4, 4, 2));
5591         }
5592 
5593         {
5594             auto date = Date(-1, 1, 1);
5595             date.roll!"months"(-1);
5596             assert(date == Date(-1, 12, 1));
5597             date.roll!"months"(1);
5598             assert(date == Date(-1, 1, 1));
5599         }
5600 
5601         {
5602             auto date = Date(-4, 1, 1);
5603             date.roll!"months"(-48);
5604             assert(date == Date(-4, 1, 1));
5605             date.roll!"months"(48);
5606             assert(date == Date(-4, 1, 1));
5607         }
5608 
5609         {
5610             auto date = Date(-4, 3, 31);
5611             date.roll!"months"(-49);
5612             assert(date == Date(-4, 3, 2));
5613             date.roll!"months"(49);
5614             assert(date == Date(-4, 4, 2));
5615         }
5616 
5617         {
5618             auto date = Date(-4, 3, 31);
5619             date.roll!"months"(-85);
5620             assert(date == Date(-4, 3, 2));
5621             date.roll!"months"(85);
5622             assert(date == Date(-4, 4, 2));
5623         }
5624 
5625         {
5626             auto date = Date(-3, 3, 31);
5627             date.roll!"months"(85).roll!"months"(-83);
5628             assert(date == Date(-3, 6, 1));
5629         }
5630 
5631         const cdate = Date(1999, 7, 6);
5632         immutable idate = Date(1999, 7, 6);
5633         static assert(!__traits(compiles, cdate.roll!"months"(3)));
5634         static assert(!__traits(compiles, idate.roll!"months"(3)));
5635     }
5636 
5637     // Test roll!"months"() with AllowDayOverflow.no
5638     @safe unittest
5639     {
5640         // Test A.D.
5641         {
5642             auto date = Date(1999, 7, 6);
5643             date.roll!"months"(3, AllowDayOverflow.no);
5644             assert(date == Date(1999, 10, 6));
5645             date.roll!"months"(-4, AllowDayOverflow.no);
5646             assert(date == Date(1999, 6, 6));
5647         }
5648 
5649         {
5650             auto date = Date(1999, 7, 6);
5651             date.roll!"months"(6, AllowDayOverflow.no);
5652             assert(date == Date(1999, 1, 6));
5653             date.roll!"months"(-6, AllowDayOverflow.no);
5654             assert(date == Date(1999, 7, 6));
5655         }
5656 
5657         {
5658             auto date = Date(1999, 7, 6);
5659             date.roll!"months"(27, AllowDayOverflow.no);
5660             assert(date == Date(1999, 10, 6));
5661             date.roll!"months"(-28, AllowDayOverflow.no);
5662             assert(date == Date(1999, 6, 6));
5663         }
5664 
5665         {
5666             auto date = Date(1999, 5, 31);
5667             date.roll!"months"(1, AllowDayOverflow.no);
5668             assert(date == Date(1999, 6, 30));
5669         }
5670 
5671         {
5672             auto date = Date(1999, 5, 31);
5673             date.roll!"months"(-1, AllowDayOverflow.no);
5674             assert(date == Date(1999, 4, 30));
5675         }
5676 
5677         {
5678             auto date = Date(1999, 2, 28);
5679             date.roll!"months"(12, AllowDayOverflow.no);
5680             assert(date == Date(1999, 2, 28));
5681         }
5682 
5683         {
5684             auto date = Date(2000, 2, 29);
5685             date.roll!"months"(12, AllowDayOverflow.no);
5686             assert(date == Date(2000, 2, 29));
5687         }
5688 
5689         {
5690             auto date = Date(1999, 7, 31);
5691             date.roll!"months"(1, AllowDayOverflow.no);
5692             assert(date == Date(1999, 8, 31));
5693             date.roll!"months"(1, AllowDayOverflow.no);
5694             assert(date == Date(1999, 9, 30));
5695         }
5696 
5697         {
5698             auto date = Date(1998, 8, 31);
5699             date.roll!"months"(13, AllowDayOverflow.no);
5700             assert(date == Date(1998, 9, 30));
5701             date.roll!"months"(-13, AllowDayOverflow.no);
5702             assert(date == Date(1998, 8, 30));
5703         }
5704 
5705         {
5706             auto date = Date(1997, 12, 31);
5707             date.roll!"months"(13, AllowDayOverflow.no);
5708             assert(date == Date(1997, 1, 31));
5709             date.roll!"months"(-13, AllowDayOverflow.no);
5710             assert(date == Date(1997, 12, 31));
5711         }
5712 
5713         {
5714             auto date = Date(1997, 12, 31);
5715             date.roll!"months"(14, AllowDayOverflow.no);
5716             assert(date == Date(1997, 2, 28));
5717             date.roll!"months"(-14, AllowDayOverflow.no);
5718             assert(date == Date(1997, 12, 28));
5719         }
5720 
5721         {
5722             auto date = Date(1998, 12, 31);
5723             date.roll!"months"(14, AllowDayOverflow.no);
5724             assert(date == Date(1998, 2, 28));
5725             date.roll!"months"(-14, AllowDayOverflow.no);
5726             assert(date == Date(1998, 12, 28));
5727         }
5728 
5729         {
5730             auto date = Date(1999, 12, 31);
5731             date.roll!"months"(14, AllowDayOverflow.no);
5732             assert(date == Date(1999, 2, 28));
5733             date.roll!"months"(-14, AllowDayOverflow.no);
5734             assert(date == Date(1999, 12, 28));
5735         }
5736 
5737         // Test B.C.
5738         {
5739             auto date = Date(-1999, 7, 6);
5740             date.roll!"months"(3, AllowDayOverflow.no);
5741             assert(date == Date(-1999, 10, 6));
5742             date.roll!"months"(-4, AllowDayOverflow.no);
5743             assert(date == Date(-1999, 6, 6));
5744         }
5745 
5746         {
5747             auto date = Date(-1999, 7, 6);
5748             date.roll!"months"(6, AllowDayOverflow.no);
5749             assert(date == Date(-1999, 1, 6));
5750             date.roll!"months"(-6, AllowDayOverflow.no);
5751             assert(date == Date(-1999, 7, 6));
5752         }
5753 
5754         {
5755             auto date = Date(-1999, 7, 6);
5756             date.roll!"months"(-27, AllowDayOverflow.no);
5757             assert(date == Date(-1999, 4, 6));
5758             date.roll!"months"(28, AllowDayOverflow.no);
5759             assert(date == Date(-1999, 8, 6));
5760         }
5761 
5762         {
5763             auto date = Date(-1999, 5, 31);
5764             date.roll!"months"(1, AllowDayOverflow.no);
5765             assert(date == Date(-1999, 6, 30));
5766         }
5767 
5768         {
5769             auto date = Date(-1999, 5, 31);
5770             date.roll!"months"(-1, AllowDayOverflow.no);
5771             assert(date == Date(-1999, 4, 30));
5772         }
5773 
5774         {
5775             auto date = Date(-1999, 2, 28);
5776             date.roll!"months"(-12, AllowDayOverflow.no);
5777             assert(date == Date(-1999, 2, 28));
5778         }
5779 
5780         {
5781             auto date = Date(-2000, 2, 29);
5782             date.roll!"months"(-12, AllowDayOverflow.no);
5783             assert(date == Date(-2000, 2, 29));
5784         }
5785 
5786         {
5787             auto date = Date(-1999, 7, 31);
5788             date.roll!"months"(1, AllowDayOverflow.no);
5789             assert(date == Date(-1999, 8, 31));
5790             date.roll!"months"(1, AllowDayOverflow.no);
5791             assert(date == Date(-1999, 9, 30));
5792         }
5793 
5794         {
5795             auto date = Date(-1998, 8, 31);
5796             date.roll!"months"(13, AllowDayOverflow.no);
5797             assert(date == Date(-1998, 9, 30));
5798             date.roll!"months"(-13, AllowDayOverflow.no);
5799             assert(date == Date(-1998, 8, 30));
5800         }
5801 
5802         {
5803             auto date = Date(-1997, 12, 31);
5804             date.roll!"months"(13, AllowDayOverflow.no);
5805             assert(date == Date(-1997, 1, 31));
5806             date.roll!"months"(-13, AllowDayOverflow.no);
5807             assert(date == Date(-1997, 12, 31));
5808         }
5809 
5810         {
5811             auto date = Date(-1997, 12, 31);
5812             date.roll!"months"(14, AllowDayOverflow.no);
5813             assert(date == Date(-1997, 2, 28));
5814             date.roll!"months"(-14, AllowDayOverflow.no);
5815             assert(date == Date(-1997, 12, 28));
5816         }
5817 
5818         {
5819             auto date = Date(-2002, 12, 31);
5820             date.roll!"months"(14, AllowDayOverflow.no);
5821             assert(date == Date(-2002, 2, 28));
5822             date.roll!"months"(-14, AllowDayOverflow.no);
5823             assert(date == Date(-2002, 12, 28));
5824         }
5825 
5826         {
5827             auto date = Date(-2001, 12, 31);
5828             date.roll!"months"(14, AllowDayOverflow.no);
5829             assert(date == Date(-2001, 2, 28));
5830             date.roll!"months"(-14, AllowDayOverflow.no);
5831             assert(date == Date(-2001, 12, 28));
5832         }
5833 
5834         // Test Both
5835         {
5836             auto date = Date(1, 1, 1);
5837             date.roll!"months"(-1, AllowDayOverflow.no);
5838             assert(date == Date(1, 12, 1));
5839             date.roll!"months"(1, AllowDayOverflow.no);
5840             assert(date == Date(1, 1, 1));
5841         }
5842 
5843         {
5844             auto date = Date(4, 1, 1);
5845             date.roll!"months"(-48, AllowDayOverflow.no);
5846             assert(date == Date(4, 1, 1));
5847             date.roll!"months"(48, AllowDayOverflow.no);
5848             assert(date == Date(4, 1, 1));
5849         }
5850 
5851         {
5852             auto date = Date(4, 3, 31);
5853             date.roll!"months"(-49, AllowDayOverflow.no);
5854             assert(date == Date(4, 2, 29));
5855             date.roll!"months"(49, AllowDayOverflow.no);
5856             assert(date == Date(4, 3, 29));
5857         }
5858 
5859         {
5860             auto date = Date(4, 3, 31);
5861             date.roll!"months"(-85, AllowDayOverflow.no);
5862             assert(date == Date(4, 2, 29));
5863             date.roll!"months"(85, AllowDayOverflow.no);
5864             assert(date == Date(4, 3, 29));
5865         }
5866 
5867         {
5868             auto date = Date(-1, 1, 1);
5869             date.roll!"months"(-1, AllowDayOverflow.no);
5870             assert(date == Date(-1, 12, 1));
5871             date.roll!"months"(1, AllowDayOverflow.no);
5872             assert(date == Date(-1, 1, 1));
5873         }
5874 
5875         {
5876             auto date = Date(-4, 1, 1);
5877             date.roll!"months"(-48, AllowDayOverflow.no);
5878             assert(date == Date(-4, 1, 1));
5879             date.roll!"months"(48, AllowDayOverflow.no);
5880             assert(date == Date(-4, 1, 1));
5881         }
5882 
5883         {
5884             auto date = Date(-4, 3, 31);
5885             date.roll!"months"(-49, AllowDayOverflow.no);
5886             assert(date == Date(-4, 2, 29));
5887             date.roll!"months"(49, AllowDayOverflow.no);
5888             assert(date == Date(-4, 3, 29));
5889         }
5890 
5891         {
5892             auto date = Date(-4, 3, 31);
5893             date.roll!"months"(-85, AllowDayOverflow.no);
5894             assert(date == Date(-4, 2, 29));
5895             date.roll!"months"(85, AllowDayOverflow.no);
5896             assert(date == Date(-4, 3, 29));
5897         }
5898 
5899         {
5900             auto date = Date(-3, 3, 31);
5901             date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
5902             assert(date == Date(-3, 5, 30));
5903         }
5904     }
5905 
5906 
5907     /++
5908         Adds the given number of units to this $(LREF Date), mutating it. A
5909         negative number will subtract.
5910 
5911         The difference between rolling and adding is that rolling does not
5912         affect larger units. For instance, rolling a $(LREF Date) one
5913         year's worth of days gets the exact same $(LREF Date).
5914 
5915         The only accepted units are `"days"`.
5916 
5917         Params:
5918             units = The units to add. Must be `"days"`.
5919             days  = The number of days to add to this $(LREF Date).
5920 
5921         Returns:
5922             A reference to the `Date` (`this`).
5923       +/
5924     ref Date roll(string units)(long days) @safe pure nothrow @nogc
5925     if (units == "days")
5926     {
5927         immutable limit = maxDay(_year, _month);
5928         days %= limit;
5929         auto newDay = _day + days;
5930 
5931         if (days < 0)
5932         {
5933             if (newDay < 1)
5934                 newDay += limit;
5935         }
5936         else if (newDay > limit)
5937             newDay -= limit;
5938 
5939         _day = cast(ubyte) newDay;
5940         return this;
5941     }
5942 
5943     ///
5944     @safe unittest
5945     {
5946         auto d = Date(2010, 1, 1);
5947         d.roll!"days"(1);
5948         assert(d == Date(2010, 1, 2));
5949         d.roll!"days"(365);
5950         assert(d == Date(2010, 1, 26));
5951         d.roll!"days"(-32);
5952         assert(d == Date(2010, 1, 25));
5953     }
5954 
5955     @safe unittest
5956     {
5957         // Test A.D.
5958         {
5959             auto date = Date(1999, 2, 28);
5960             date.roll!"days"(1);
5961             assert(date == Date(1999, 2, 1));
5962             date.roll!"days"(-1);
5963             assert(date == Date(1999, 2, 28));
5964         }
5965 
5966         {
5967             auto date = Date(2000, 2, 28);
5968             date.roll!"days"(1);
5969             assert(date == Date(2000, 2, 29));
5970             date.roll!"days"(1);
5971             assert(date == Date(2000, 2, 1));
5972             date.roll!"days"(-1);
5973             assert(date == Date(2000, 2, 29));
5974         }
5975 
5976         {
5977             auto date = Date(1999, 6, 30);
5978             date.roll!"days"(1);
5979             assert(date == Date(1999, 6, 1));
5980             date.roll!"days"(-1);
5981             assert(date == Date(1999, 6, 30));
5982         }
5983 
5984         {
5985             auto date = Date(1999, 7, 31);
5986             date.roll!"days"(1);
5987             assert(date == Date(1999, 7, 1));
5988             date.roll!"days"(-1);
5989             assert(date == Date(1999, 7, 31));
5990         }
5991 
5992         {
5993             auto date = Date(1999, 1, 1);
5994             date.roll!"days"(-1);
5995             assert(date == Date(1999, 1, 31));
5996             date.roll!"days"(1);
5997             assert(date == Date(1999, 1, 1));
5998         }
5999 
6000         {
6001             auto date = Date(1999, 7, 6);
6002             date.roll!"days"(9);
6003             assert(date == Date(1999, 7, 15));
6004             date.roll!"days"(-11);
6005             assert(date == Date(1999, 7, 4));
6006             date.roll!"days"(30);
6007             assert(date == Date(1999, 7, 3));
6008             date.roll!"days"(-3);
6009             assert(date == Date(1999, 7, 31));
6010         }
6011 
6012         {
6013             auto date = Date(1999, 7, 6);
6014             date.roll!"days"(365);
6015             assert(date == Date(1999, 7, 30));
6016             date.roll!"days"(-365);
6017             assert(date == Date(1999, 7, 6));
6018             date.roll!"days"(366);
6019             assert(date == Date(1999, 7, 31));
6020             date.roll!"days"(730);
6021             assert(date == Date(1999, 7, 17));
6022             date.roll!"days"(-1096);
6023             assert(date == Date(1999, 7, 6));
6024         }
6025 
6026         {
6027             auto date = Date(1999, 2, 6);
6028             date.roll!"days"(365);
6029             assert(date == Date(1999, 2, 7));
6030             date.roll!"days"(-365);
6031             assert(date == Date(1999, 2, 6));
6032             date.roll!"days"(366);
6033             assert(date == Date(1999, 2, 8));
6034             date.roll!"days"(730);
6035             assert(date == Date(1999, 2, 10));
6036             date.roll!"days"(-1096);
6037             assert(date == Date(1999, 2, 6));
6038         }
6039 
6040         // Test B.C.
6041         {
6042             auto date = Date(-1999, 2, 28);
6043             date.roll!"days"(1);
6044             assert(date == Date(-1999, 2, 1));
6045             date.roll!"days"(-1);
6046             assert(date == Date(-1999, 2, 28));
6047         }
6048 
6049         {
6050             auto date = Date(-2000, 2, 28);
6051             date.roll!"days"(1);
6052             assert(date == Date(-2000, 2, 29));
6053             date.roll!"days"(1);
6054             assert(date == Date(-2000, 2, 1));
6055             date.roll!"days"(-1);
6056             assert(date == Date(-2000, 2, 29));
6057         }
6058 
6059         {
6060             auto date = Date(-1999, 6, 30);
6061             date.roll!"days"(1);
6062             assert(date == Date(-1999, 6, 1));
6063             date.roll!"days"(-1);
6064             assert(date == Date(-1999, 6, 30));
6065         }
6066 
6067         {
6068             auto date = Date(-1999, 7, 31);
6069             date.roll!"days"(1);
6070             assert(date == Date(-1999, 7, 1));
6071             date.roll!"days"(-1);
6072             assert(date == Date(-1999, 7, 31));
6073         }
6074 
6075         {
6076             auto date = Date(-1999, 1, 1);
6077             date.roll!"days"(-1);
6078             assert(date == Date(-1999, 1, 31));
6079             date.roll!"days"(1);
6080             assert(date == Date(-1999, 1, 1));
6081         }
6082 
6083         {
6084             auto date = Date(-1999, 7, 6);
6085             date.roll!"days"(9);
6086             assert(date == Date(-1999, 7, 15));
6087             date.roll!"days"(-11);
6088             assert(date == Date(-1999, 7, 4));
6089             date.roll!"days"(30);
6090             assert(date == Date(-1999, 7, 3));
6091             date.roll!"days"(-3);
6092             assert(date == Date(-1999, 7, 31));
6093         }
6094 
6095         {
6096             auto date = Date(-1999, 7, 6);
6097             date.roll!"days"(365);
6098             assert(date == Date(-1999, 7, 30));
6099             date.roll!"days"(-365);
6100             assert(date == Date(-1999, 7, 6));
6101             date.roll!"days"(366);
6102             assert(date == Date(-1999, 7, 31));
6103             date.roll!"days"(730);
6104             assert(date == Date(-1999, 7, 17));
6105             date.roll!"days"(-1096);
6106             assert(date == Date(-1999, 7, 6));
6107         }
6108 
6109         // Test Both
6110         {
6111             auto date = Date(1, 7, 6);
6112             date.roll!"days"(-365);
6113             assert(date == Date(1, 7, 13));
6114             date.roll!"days"(365);
6115             assert(date == Date(1, 7, 6));
6116             date.roll!"days"(-731);
6117             assert(date == Date(1, 7, 19));
6118             date.roll!"days"(730);
6119             assert(date == Date(1, 7, 5));
6120         }
6121 
6122         {
6123             auto date = Date(0, 7, 6);
6124             date.roll!"days"(-365);
6125             assert(date == Date(0, 7, 13));
6126             date.roll!"days"(365);
6127             assert(date == Date(0, 7, 6));
6128             date.roll!"days"(-731);
6129             assert(date == Date(0, 7, 19));
6130             date.roll!"days"(730);
6131             assert(date == Date(0, 7, 5));
6132         }
6133 
6134         {
6135             auto date = Date(0, 7, 6);
6136             date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
6137             assert(date == Date(0, 7, 8));
6138         }
6139 
6140         const cdate = Date(1999, 7, 6);
6141         immutable idate = Date(1999, 7, 6);
6142         static assert(!__traits(compiles, cdate.roll!"days"(12)));
6143         static assert(!__traits(compiles, idate.roll!"days"(12)));
6144     }
6145 
6146     import core.time : Duration;
6147     /++
6148         Gives the result of adding or subtracting a $(REF Duration, core,time)
6149         from this $(LREF Date).
6150 
6151         The legal types of arithmetic for $(LREF Date) using this operator are
6152 
6153         $(BOOKTABLE,
6154         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6155         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6156         $(TR $(TD Duration) $(TD +) $(TD Date) $(TD -->) $(TD Date))
6157         )
6158 
6159         Params:
6160             duration = The $(REF Duration, core,time) to add to or subtract from
6161                        this $(LREF Date).
6162       +/
6163     Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
6164     if (op == "+" || op == "-")
6165     {
6166         Date retval = this;
6167         immutable days = duration.total!"days";
6168         mixin("return retval._addDays(" ~ op ~ "days);");
6169     }
6170 
6171 
6172     /// ditto
6173     Date opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
6174     if (op == "+")
6175     {
6176         return this + duration;
6177     }
6178 
6179     ///
6180     @safe unittest
6181     {
6182         import core.time : days;
6183 
6184         assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
6185         assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
6186 
6187         assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
6188         assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
6189 
6190         assert(Date(2004, 2, 26) + days(4) == days(4) + Date(2004, 2, 26));
6191     }
6192 
6193     @safe unittest
6194     {
6195         auto date = Date(1999, 7, 6);
6196 
6197         import core.time : dur;
6198         assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
6199         assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
6200         assert(date + dur!"days"(7) == Date(1999, 7, 13));
6201         assert(date + dur!"days"(-7) == Date(1999, 6, 29));
6202 
6203         assert(date + dur!"hours"(24) == Date(1999, 7, 7));
6204         assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
6205         assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
6206         assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
6207         assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
6208         assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6209         assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6210         assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6211         assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6212         assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6213         assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6214         assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6215 
6216         assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
6217         assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
6218         assert(date - dur!"days"(-7) == Date(1999, 7, 13));
6219         assert(date - dur!"days"(7) == Date(1999, 6, 29));
6220 
6221         assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
6222         assert(date - dur!"hours"(24) == Date(1999, 7, 5));
6223         assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
6224         assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
6225         assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6226         assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
6227         assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6228         assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6229         assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6230         assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6231         assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6232         assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6233 
6234         auto duration = dur!"days"(12);
6235         const cdate = Date(1999, 7, 6);
6236         immutable idate = Date(1999, 7, 6);
6237         assert(date + duration == Date(1999, 7, 18));
6238         assert(cdate + duration == Date(1999, 7, 18));
6239         assert(idate + duration == Date(1999, 7, 18));
6240 
6241         assert(date - duration == Date(1999, 6, 24));
6242         assert(cdate - duration == Date(1999, 6, 24));
6243         assert(idate - duration == Date(1999, 6, 24));
6244     }
6245 
6246 
6247     /++
6248         Gives the result of adding or subtracting a $(REF Duration, core,time)
6249         from this $(LREF Date), as well as assigning the result to this
6250         $(LREF Date).
6251 
6252         The legal types of arithmetic for $(LREF Date) using this operator are
6253 
6254         $(BOOKTABLE,
6255         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6256         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6257         )
6258 
6259         Params:
6260             duration = The $(REF Duration, core,time) to add to or subtract from
6261                        this $(LREF Date).
6262       +/
6263     ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
6264     if (op == "+" || op == "-")
6265     {
6266         immutable days = duration.total!"days";
6267         mixin("return _addDays(" ~ op ~ "days);");
6268     }
6269 
6270     @safe unittest
6271     {
6272         import core.time : dur;
6273         assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
6274         assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
6275         assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
6276         assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
6277 
6278         assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
6279         assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
6280         assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
6281         assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
6282         assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
6283         assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6284         assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6285         assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6286         assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6287         assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6288         assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6289         assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6290 
6291         assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
6292         assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
6293         assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
6294         assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
6295 
6296         assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
6297         assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
6298         assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
6299         assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
6300         assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6301         assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
6302         assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6303         assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6304         assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6305         assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6306         assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6307         assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6308 
6309         {
6310             auto date = Date(0, 1, 31);
6311             (date += dur!"days"(507)) += dur!"days"(-2);
6312             assert(date == Date(1, 6, 19));
6313         }
6314 
6315         auto duration = dur!"days"(12);
6316         auto date = Date(1999, 7, 6);
6317         const cdate = Date(1999, 7, 6);
6318         immutable idate = Date(1999, 7, 6);
6319         date += duration;
6320         static assert(!__traits(compiles, cdate += duration));
6321         static assert(!__traits(compiles, idate += duration));
6322 
6323         date -= duration;
6324         static assert(!__traits(compiles, cdate -= duration));
6325         static assert(!__traits(compiles, idate -= duration));
6326     }
6327 
6328 
6329     /++
6330         Gives the difference between two $(LREF Date)s.
6331 
6332         The legal types of arithmetic for $(LREF Date) using this operator are
6333 
6334         $(BOOKTABLE,
6335         $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
6336         )
6337       +/
6338     Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
6339     if (op == "-")
6340     {
6341         import core.time : dur;
6342         return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
6343     }
6344 
6345     @safe unittest
6346     {
6347         auto date = Date(1999, 7, 6);
6348 
6349         import core.time : dur;
6350         assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
6351         assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
6352         assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
6353         assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
6354         assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
6355         assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
6356 
6357         const cdate = Date(1999, 7, 6);
6358         immutable idate = Date(1999, 7, 6);
6359         assert(date - date == Duration.zero);
6360         assert(cdate - date == Duration.zero);
6361         assert(idate - date == Duration.zero);
6362 
6363         assert(date - cdate == Duration.zero);
6364         assert(cdate - cdate == Duration.zero);
6365         assert(idate - cdate == Duration.zero);
6366 
6367         assert(date - idate == Duration.zero);
6368         assert(cdate - idate == Duration.zero);
6369         assert(idate - idate == Duration.zero);
6370     }
6371 
6372 
6373     /++
6374         Returns the difference between the two $(LREF Date)s in months.
6375 
6376         To get the difference in years, subtract the year property
6377         of two $(LREF Date)s. To get the difference in days or weeks,
6378         subtract the $(LREF Date)s themselves and use the
6379         $(REF Duration, core,time) that results. Because converting between
6380         months and smaller units requires a specific date (which
6381         $(REF Duration, core,time)s don't have), getting the difference in
6382         months requires some math using both the year and month properties, so
6383         this is a convenience function for getting the difference in months.
6384 
6385         Note that the number of days in the months or how far into the month
6386         either $(LREF Date) is is irrelevant. It is the difference in the month
6387         property combined with the difference in years * 12. So, for instance,
6388         December 31st and January 1st are one month apart just as December 1st
6389         and January 31st are one month apart.
6390 
6391         Params:
6392             rhs = The $(LREF Date) to subtract from this one.
6393       +/
6394     int diffMonths(Date rhs) const @safe pure nothrow @nogc
6395     {
6396         immutable yearDiff = _year - rhs._year;
6397         immutable monthDiff = _month - rhs._month;
6398 
6399         return yearDiff * 12 + monthDiff;
6400     }
6401 
6402     ///
6403     @safe unittest
6404     {
6405         assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
6406         assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
6407         assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
6408         assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
6409     }
6410 
6411     @safe unittest
6412     {
6413         auto date = Date(1999, 7, 6);
6414 
6415         // Test A.D.
6416         assert(date.diffMonths(Date(1998, 6, 5)) == 13);
6417         assert(date.diffMonths(Date(1998, 7, 5)) == 12);
6418         assert(date.diffMonths(Date(1998, 8, 5)) == 11);
6419         assert(date.diffMonths(Date(1998, 9, 5)) == 10);
6420         assert(date.diffMonths(Date(1998, 10, 5)) == 9);
6421         assert(date.diffMonths(Date(1998, 11, 5)) == 8);
6422         assert(date.diffMonths(Date(1998, 12, 5)) == 7);
6423         assert(date.diffMonths(Date(1999, 1, 5)) == 6);
6424         assert(date.diffMonths(Date(1999, 2, 6)) == 5);
6425         assert(date.diffMonths(Date(1999, 3, 6)) == 4);
6426         assert(date.diffMonths(Date(1999, 4, 6)) == 3);
6427         assert(date.diffMonths(Date(1999, 5, 6)) == 2);
6428         assert(date.diffMonths(Date(1999, 6, 6)) == 1);
6429         assert(date.diffMonths(date) == 0);
6430         assert(date.diffMonths(Date(1999, 8, 6)) == -1);
6431         assert(date.diffMonths(Date(1999, 9, 6)) == -2);
6432         assert(date.diffMonths(Date(1999, 10, 6)) == -3);
6433         assert(date.diffMonths(Date(1999, 11, 6)) == -4);
6434         assert(date.diffMonths(Date(1999, 12, 6)) == -5);
6435         assert(date.diffMonths(Date(2000, 1, 6)) == -6);
6436         assert(date.diffMonths(Date(2000, 2, 6)) == -7);
6437         assert(date.diffMonths(Date(2000, 3, 6)) == -8);
6438         assert(date.diffMonths(Date(2000, 4, 6)) == -9);
6439         assert(date.diffMonths(Date(2000, 5, 6)) == -10);
6440         assert(date.diffMonths(Date(2000, 6, 6)) == -11);
6441         assert(date.diffMonths(Date(2000, 7, 6)) == -12);
6442         assert(date.diffMonths(Date(2000, 8, 6)) == -13);
6443 
6444         assert(Date(1998, 6, 5).diffMonths(date) == -13);
6445         assert(Date(1998, 7, 5).diffMonths(date) == -12);
6446         assert(Date(1998, 8, 5).diffMonths(date) == -11);
6447         assert(Date(1998, 9, 5).diffMonths(date) == -10);
6448         assert(Date(1998, 10, 5).diffMonths(date) == -9);
6449         assert(Date(1998, 11, 5).diffMonths(date) == -8);
6450         assert(Date(1998, 12, 5).diffMonths(date) == -7);
6451         assert(Date(1999, 1, 5).diffMonths(date) == -6);
6452         assert(Date(1999, 2, 6).diffMonths(date) == -5);
6453         assert(Date(1999, 3, 6).diffMonths(date) == -4);
6454         assert(Date(1999, 4, 6).diffMonths(date) == -3);
6455         assert(Date(1999, 5, 6).diffMonths(date) == -2);
6456         assert(Date(1999, 6, 6).diffMonths(date) == -1);
6457         assert(Date(1999, 8, 6).diffMonths(date) == 1);
6458         assert(Date(1999, 9, 6).diffMonths(date) == 2);
6459         assert(Date(1999, 10, 6).diffMonths(date) == 3);
6460         assert(Date(1999, 11, 6).diffMonths(date) == 4);
6461         assert(Date(1999, 12, 6).diffMonths(date) == 5);
6462         assert(Date(2000, 1, 6).diffMonths(date) == 6);
6463         assert(Date(2000, 2, 6).diffMonths(date) == 7);
6464         assert(Date(2000, 3, 6).diffMonths(date) == 8);
6465         assert(Date(2000, 4, 6).diffMonths(date) == 9);
6466         assert(Date(2000, 5, 6).diffMonths(date) == 10);
6467         assert(Date(2000, 6, 6).diffMonths(date) == 11);
6468         assert(Date(2000, 7, 6).diffMonths(date) == 12);
6469         assert(Date(2000, 8, 6).diffMonths(date) == 13);
6470 
6471         assert(date.diffMonths(Date(1999, 6, 30)) == 1);
6472         assert(date.diffMonths(Date(1999, 7, 1)) == 0);
6473         assert(date.diffMonths(Date(1999, 7, 6)) == 0);
6474         assert(date.diffMonths(Date(1999, 7, 11)) == 0);
6475         assert(date.diffMonths(Date(1999, 7, 16)) == 0);
6476         assert(date.diffMonths(Date(1999, 7, 21)) == 0);
6477         assert(date.diffMonths(Date(1999, 7, 26)) == 0);
6478         assert(date.diffMonths(Date(1999, 7, 31)) == 0);
6479         assert(date.diffMonths(Date(1999, 8, 1)) == -1);
6480 
6481         assert(date.diffMonths(Date(1990, 6, 30)) == 109);
6482         assert(date.diffMonths(Date(1990, 7, 1)) == 108);
6483         assert(date.diffMonths(Date(1990, 7, 6)) == 108);
6484         assert(date.diffMonths(Date(1990, 7, 11)) == 108);
6485         assert(date.diffMonths(Date(1990, 7, 16)) == 108);
6486         assert(date.diffMonths(Date(1990, 7, 21)) == 108);
6487         assert(date.diffMonths(Date(1990, 7, 26)) == 108);
6488         assert(date.diffMonths(Date(1990, 7, 31)) == 108);
6489         assert(date.diffMonths(Date(1990, 8, 1)) == 107);
6490 
6491         assert(Date(1999, 6, 30).diffMonths(date) == -1);
6492         assert(Date(1999, 7, 1).diffMonths(date) == 0);
6493         assert(Date(1999, 7, 6).diffMonths(date) == 0);
6494         assert(Date(1999, 7, 11).diffMonths(date) == 0);
6495         assert(Date(1999, 7, 16).diffMonths(date) == 0);
6496         assert(Date(1999, 7, 21).diffMonths(date) == 0);
6497         assert(Date(1999, 7, 26).diffMonths(date) == 0);
6498         assert(Date(1999, 7, 31).diffMonths(date) == 0);
6499         assert(Date(1999, 8, 1).diffMonths(date) == 1);
6500 
6501         assert(Date(1990, 6, 30).diffMonths(date) == -109);
6502         assert(Date(1990, 7, 1).diffMonths(date) == -108);
6503         assert(Date(1990, 7, 6).diffMonths(date) == -108);
6504         assert(Date(1990, 7, 11).diffMonths(date) == -108);
6505         assert(Date(1990, 7, 16).diffMonths(date) == -108);
6506         assert(Date(1990, 7, 21).diffMonths(date) == -108);
6507         assert(Date(1990, 7, 26).diffMonths(date) == -108);
6508         assert(Date(1990, 7, 31).diffMonths(date) == -108);
6509         assert(Date(1990, 8, 1).diffMonths(date) == -107);
6510 
6511         // Test B.C.
6512         auto dateBC = Date(-1999, 7, 6);
6513 
6514         assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
6515         assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
6516         assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
6517         assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
6518         assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
6519         assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
6520         assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
6521         assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
6522         assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
6523         assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
6524         assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
6525         assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
6526         assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
6527         assert(dateBC.diffMonths(dateBC) == 0);
6528         assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
6529         assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
6530         assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
6531         assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
6532         assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
6533         assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
6534         assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
6535         assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
6536         assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
6537         assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
6538         assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
6539         assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
6540         assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
6541 
6542         assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
6543         assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
6544         assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
6545         assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
6546         assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
6547         assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
6548         assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
6549         assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
6550         assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
6551         assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
6552         assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
6553         assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
6554         assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
6555         assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
6556         assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
6557         assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
6558         assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
6559         assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
6560         assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
6561         assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
6562         assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
6563         assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
6564         assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
6565         assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
6566         assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
6567         assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
6568 
6569         assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
6570         assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
6571         assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
6572         assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
6573         assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
6574         assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
6575         assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
6576         assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
6577         assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
6578 
6579         assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
6580         assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
6581         assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
6582         assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
6583         assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
6584         assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
6585         assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
6586         assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
6587         assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
6588 
6589         assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
6590         assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
6591         assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
6592         assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
6593         assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
6594         assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
6595         assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
6596         assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
6597         assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
6598 
6599         assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
6600         assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
6601         assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
6602         assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
6603         assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
6604         assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
6605         assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
6606         assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
6607         assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
6608 
6609         // Test Both
6610         assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
6611         assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
6612 
6613         const cdate = Date(1999, 7, 6);
6614         immutable idate = Date(1999, 7, 6);
6615         assert(date.diffMonths(date) == 0);
6616         assert(cdate.diffMonths(date) == 0);
6617         assert(idate.diffMonths(date) == 0);
6618 
6619         assert(date.diffMonths(cdate) == 0);
6620         assert(cdate.diffMonths(cdate) == 0);
6621         assert(idate.diffMonths(cdate) == 0);
6622 
6623         assert(date.diffMonths(idate) == 0);
6624         assert(cdate.diffMonths(idate) == 0);
6625         assert(idate.diffMonths(idate) == 0);
6626     }
6627 
6628 
6629     /++
6630         Whether this $(LREF Date) is in a leap year.
6631      +/
6632     @property bool isLeapYear() const @safe pure nothrow @nogc
6633     {
6634         return yearIsLeapYear(_year);
6635     }
6636 
6637     @safe unittest
6638     {
6639         auto date = Date(1999, 7, 6);
6640         const cdate = Date(1999, 7, 6);
6641         immutable idate = Date(1999, 7, 6);
6642         static assert(!__traits(compiles, date.isLeapYear = true));
6643         static assert(!__traits(compiles, cdate.isLeapYear = true));
6644         static assert(!__traits(compiles, idate.isLeapYear = true));
6645     }
6646 
6647 
6648     /++
6649         Day of the week this $(LREF Date) is on.
6650       +/
6651     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
6652     {
6653         return getDayOfWeek(dayOfGregorianCal);
6654     }
6655 
6656     @safe unittest
6657     {
6658         const cdate = Date(1999, 7, 6);
6659         immutable idate = Date(1999, 7, 6);
6660         assert(cdate.dayOfWeek == DayOfWeek.tue);
6661         static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
6662         assert(idate.dayOfWeek == DayOfWeek.tue);
6663         static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
6664     }
6665 
6666 
6667     /++
6668         Day of the year this $(LREF Date) is on.
6669       +/
6670     @property ushort dayOfYear() const @safe pure nothrow @nogc
6671     {
6672         if (_month >= Month.jan && _month <= Month.dec)
6673         {
6674             immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6675             auto monthIndex = _month - Month.jan;
6676 
6677             return cast(ushort)(lastDay[monthIndex] + _day);
6678         }
6679         assert(0, "Invalid month.");
6680     }
6681 
6682     ///
6683     @safe unittest
6684     {
6685         assert(Date(1999, 1, 1).dayOfYear == 1);
6686         assert(Date(1999, 12, 31).dayOfYear == 365);
6687         assert(Date(2000, 12, 31).dayOfYear == 366);
6688     }
6689 
6690     @safe unittest
6691     {
6692         import std.algorithm.iteration : filter;
6693         import std.range : chain;
6694 
6695         foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6696         {
6697             foreach (doy; testDaysOfYear)
6698                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6699         }
6700 
6701         foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6702         {
6703             foreach (doy; testDaysOfLeapYear)
6704                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6705         }
6706 
6707         const cdate = Date(1999, 7, 6);
6708         immutable idate = Date(1999, 7, 6);
6709         assert(cdate.dayOfYear == 187);
6710         assert(idate.dayOfYear == 187);
6711     }
6712 
6713     /++
6714         Day of the year.
6715 
6716         Params:
6717             day = The day of the year to set which day of the year this
6718                   $(LREF Date) is on.
6719 
6720         Throws:
6721             $(REF DateTimeException,std,datetime,date) if the given day is an
6722             invalid day of the year.
6723       +/
6724     @property void dayOfYear(int day) @safe pure
6725     {
6726         setDayOfYear!true(day);
6727     }
6728 
6729     private void setDayOfYear(bool useExceptions = false)(int day)
6730     {
6731         immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6732 
6733         bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
6734         enum errorMsg = "Invalid day of the year.";
6735 
6736         static if (useExceptions)
6737         {
6738             if (dayOutOfRange) throw new DateTimeException(errorMsg);
6739         }
6740         else
6741         {
6742             assert(!dayOutOfRange, errorMsg);
6743         }
6744 
6745         foreach (i; 1 .. lastDay.length)
6746         {
6747             if (day <= lastDay[i])
6748             {
6749                 _month = cast(Month)(cast(int) Month.jan + i - 1);
6750                 _day = cast(ubyte)(day - lastDay[i - 1]);
6751                 return;
6752             }
6753         }
6754         assert(0, "Invalid day of the year.");
6755     }
6756 
6757     @safe unittest
6758     {
6759         static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
6760         {
6761             date.dayOfYear = day;
6762             assert(date.month == expected.month);
6763             assert(date.day == expected.day);
6764         }
6765 
6766         foreach (doy; testDaysOfYear)
6767         {
6768             test(Date(1999, 1, 1), doy.day, doy.md);
6769             test(Date(-1, 1, 1), doy.day, doy.md);
6770         }
6771 
6772         foreach (doy; testDaysOfLeapYear)
6773         {
6774             test(Date(2000, 1, 1), doy.day, doy.md);
6775             test(Date(-4, 1, 1), doy.day, doy.md);
6776         }
6777 
6778         const cdate = Date(1999, 7, 6);
6779         immutable idate = Date(1999, 7, 6);
6780         static assert(!__traits(compiles, cdate.dayOfYear = 187));
6781         static assert(!__traits(compiles, idate.dayOfYear = 187));
6782     }
6783 
6784 
6785     /++
6786         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6787      +/
6788     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
6789     {
6790         if (isAD)
6791         {
6792             if (_year == 1)
6793                 return dayOfYear;
6794 
6795             int years = _year - 1;
6796             auto days = (years / 400) * daysIn400Years;
6797             years %= 400;
6798 
6799             days += (years / 100) * daysIn100Years;
6800             years %= 100;
6801 
6802             days += (years / 4) * daysIn4Years;
6803             years %= 4;
6804 
6805             days += years * daysInYear;
6806 
6807             days += dayOfYear;
6808 
6809             return days;
6810         }
6811         else if (_year == 0)
6812             return dayOfYear - daysInLeapYear;
6813         else
6814         {
6815             int years = _year;
6816             auto days = (years / 400) * daysIn400Years;
6817             years %= 400;
6818 
6819             days += (years / 100) * daysIn100Years;
6820             years %= 100;
6821 
6822             days += (years / 4) * daysIn4Years;
6823             years %= 4;
6824 
6825             if (years < 0)
6826             {
6827                 days -= daysInLeapYear;
6828                 ++years;
6829 
6830                 days += years * daysInYear;
6831 
6832                 days -= daysInYear - dayOfYear;
6833             }
6834             else
6835                 days -= daysInLeapYear - dayOfYear;
6836 
6837             return days;
6838         }
6839     }
6840 
6841     ///
6842     @safe unittest
6843     {
6844         assert(Date(1, 1, 1).dayOfGregorianCal == 1);
6845         assert(Date(1, 12, 31).dayOfGregorianCal == 365);
6846         assert(Date(2, 1, 1).dayOfGregorianCal == 366);
6847 
6848         assert(Date(0, 12, 31).dayOfGregorianCal == 0);
6849         assert(Date(0, 1, 1).dayOfGregorianCal == -365);
6850         assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
6851 
6852         assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
6853         assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
6854     }
6855 
6856     @safe unittest
6857     {
6858         import std.range : chain;
6859 
6860         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
6861             assert(gd.date.dayOfGregorianCal == gd.day);
6862 
6863         auto date = Date(1999, 7, 6);
6864         const cdate = Date(1999, 7, 6);
6865         immutable idate = Date(1999, 7, 6);
6866         assert(date.dayOfGregorianCal == 729_941);
6867         assert(cdate.dayOfGregorianCal == 729_941);
6868         assert(idate.dayOfGregorianCal == 729_941);
6869     }
6870 
6871     /++
6872         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6873 
6874         Params:
6875             day = The day of the Gregorian Calendar to set this $(LREF Date) to.
6876      +/
6877     @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
6878     {
6879         this = Date(day);
6880     }
6881 
6882     ///
6883     @safe unittest
6884     {
6885         auto date = Date.init;
6886         date.dayOfGregorianCal = 1;
6887         assert(date == Date(1, 1, 1));
6888 
6889         date.dayOfGregorianCal = 365;
6890         assert(date == Date(1, 12, 31));
6891 
6892         date.dayOfGregorianCal = 366;
6893         assert(date == Date(2, 1, 1));
6894 
6895         date.dayOfGregorianCal = 0;
6896         assert(date == Date(0, 12, 31));
6897 
6898         date.dayOfGregorianCal = -365;
6899         assert(date == Date(-0, 1, 1));
6900 
6901         date.dayOfGregorianCal = -366;
6902         assert(date == Date(-1, 12, 31));
6903 
6904         date.dayOfGregorianCal = 730_120;
6905         assert(date == Date(2000, 1, 1));
6906 
6907         date.dayOfGregorianCal = 734_137;
6908         assert(date == Date(2010, 12, 31));
6909     }
6910 
6911     @safe unittest
6912     {
6913         auto date = Date(1999, 7, 6);
6914         const cdate = Date(1999, 7, 6);
6915         immutable idate = Date(1999, 7, 6);
6916         date.dayOfGregorianCal = 187;
6917         assert(date.dayOfGregorianCal == 187);
6918         static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
6919         static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
6920     }
6921 
6922 
6923     /++
6924         The ISO 8601 week and year of the year that this $(LREF Date) is in.
6925 
6926         Returns:
6927             An anonymous struct with the members $(D isoWeekYear) for the
6928             resulting year and $(D isoWeek) for the resulting ISO week.
6929 
6930         See_Also:
6931             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6932       +/
6933     @property auto isoWeekAndYear() const @safe pure nothrow
6934     {
6935         struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
6936 
6937         immutable weekday = dayOfWeek;
6938         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
6939         immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
6940 
6941         try
6942         {
6943             if (week == 53)
6944             {
6945                 switch (Date(_year + 1, 1, 1).dayOfWeek)
6946                 {
6947                     case DayOfWeek.mon:
6948                     case DayOfWeek.tue:
6949                     case DayOfWeek.wed:
6950                     case DayOfWeek.thu:
6951                         return ISOWeekAndYear(cast(short) (_year + 1), 1);
6952                     case DayOfWeek.fri:
6953                     case DayOfWeek.sat:
6954                     case DayOfWeek.sun:
6955                         return ISOWeekAndYear(_year, 53);
6956                     default:
6957                         assert(0, "Invalid ISO Week");
6958                 }
6959             }
6960             else if (week > 0)
6961                 return ISOWeekAndYear(_year, cast(ubyte) week);
6962             else
6963                 return Date(_year - 1, 12, 31).isoWeekAndYear;
6964         }
6965         catch (Exception e)
6966             assert(0, "Date's constructor threw.");
6967     }
6968 
6969     /++
6970         The ISO 8601 week of the year that this $(LREF Date) is in.
6971 
6972         See_Also:
6973             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6974       +/
6975     @property ubyte isoWeek() const @safe pure nothrow
6976     {
6977         return isoWeekAndYear().isoWeek;
6978     }
6979 
6980     @safe unittest
6981     {
6982         // Test A.D.
6983         assert(Date(2009, 12, 28).isoWeek == 53);
6984         assert(Date(2009, 12, 29).isoWeek == 53);
6985         assert(Date(2009, 12, 30).isoWeek == 53);
6986         assert(Date(2009, 12, 31).isoWeek == 53);
6987         assert(Date(2010, 1, 1).isoWeek == 53);
6988         assert(Date(2010, 1, 2).isoWeek == 53);
6989         assert(Date(2010, 1, 3).isoWeek == 53);
6990         assert(Date(2010, 1, 4).isoWeek == 1);
6991         assert(Date(2010, 1, 5).isoWeek == 1);
6992         assert(Date(2010, 1, 6).isoWeek == 1);
6993         assert(Date(2010, 1, 7).isoWeek == 1);
6994         assert(Date(2010, 1, 8).isoWeek == 1);
6995         assert(Date(2010, 1, 9).isoWeek == 1);
6996         assert(Date(2010, 1, 10).isoWeek == 1);
6997         assert(Date(2010, 1, 11).isoWeek == 2);
6998         assert(Date(2010, 12, 31).isoWeek == 52);
6999 
7000         assert(Date(2004, 12, 26).isoWeek == 52);
7001         assert(Date(2004, 12, 27).isoWeek == 53);
7002         assert(Date(2004, 12, 28).isoWeek == 53);
7003         assert(Date(2004, 12, 29).isoWeek == 53);
7004         assert(Date(2004, 12, 30).isoWeek == 53);
7005         assert(Date(2004, 12, 31).isoWeek == 53);
7006         assert(Date(2005, 1, 1).isoWeek == 53);
7007         assert(Date(2005, 1, 2).isoWeek == 53);
7008 
7009         assert(Date(2005, 12, 31).isoWeek == 52);
7010         assert(Date(2007, 1, 1).isoWeek == 1);
7011 
7012         assert(Date(2007, 12, 30).isoWeek == 52);
7013         assert(Date(2007, 12, 31).isoWeek == 1);
7014         assert(Date(2008, 1, 1).isoWeek == 1);
7015 
7016         assert(Date(2008, 12, 28).isoWeek == 52);
7017         assert(Date(2008, 12, 29).isoWeek == 1);
7018         assert(Date(2008, 12, 30).isoWeek == 1);
7019         assert(Date(2008, 12, 31).isoWeek == 1);
7020         assert(Date(2009, 1, 1).isoWeek == 1);
7021         assert(Date(2009, 1, 2).isoWeek == 1);
7022         assert(Date(2009, 1, 3).isoWeek == 1);
7023         assert(Date(2009, 1, 4).isoWeek == 1);
7024 
7025         // Test B.C.
7026         // The algorithm should work identically for both A.D. and B.C. since
7027         // it doesn't really take the year into account, so B.C. testing
7028         // probably isn't really needed.
7029         assert(Date(0, 12, 31).isoWeek == 52);
7030         assert(Date(0, 1, 4).isoWeek == 1);
7031         assert(Date(0, 1, 1).isoWeek == 52);
7032 
7033         const cdate = Date(1999, 7, 6);
7034         immutable idate = Date(1999, 7, 6);
7035         assert(cdate.isoWeek == 27);
7036         static assert(!__traits(compiles, cdate.isoWeek = 3));
7037         assert(idate.isoWeek == 27);
7038         static assert(!__traits(compiles, idate.isoWeek = 3));
7039     }
7040 
7041     /++
7042         The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
7043 
7044         May differ from $(LREF year) between 28 December and 4 January.
7045 
7046         See_Also:
7047             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
7048       +/
7049     @property short isoWeekYear() const @safe pure nothrow
7050     {
7051         return isoWeekAndYear().isoWeekYear;
7052     }
7053 
7054     @safe unittest
7055     {
7056         // Test A.D.
7057         assert(Date(2009, 12, 28).isoWeekYear == 2009);
7058         assert(Date(2009, 12, 29).isoWeekYear == 2009);
7059         assert(Date(2009, 12, 30).isoWeekYear == 2009);
7060         assert(Date(2009, 12, 31).isoWeekYear == 2009);
7061         assert(Date(2010, 1, 1).isoWeekYear == 2009);
7062         assert(Date(2010, 1, 2).isoWeekYear == 2009);
7063         assert(Date(2010, 1, 3).isoWeekYear == 2009);
7064         assert(Date(2010, 1, 4).isoWeekYear == 2010);
7065         assert(Date(2010, 1, 5).isoWeekYear == 2010);
7066         assert(Date(2010, 1, 6).isoWeekYear == 2010);
7067         assert(Date(2010, 1, 7).isoWeekYear == 2010);
7068         assert(Date(2010, 1, 8).isoWeekYear == 2010);
7069         assert(Date(2010, 1, 9).isoWeekYear == 2010);
7070         assert(Date(2010, 1, 10).isoWeekYear == 2010);
7071         assert(Date(2010, 1, 11).isoWeekYear == 2010);
7072         assert(Date(2010, 12, 31).isoWeekYear == 2010);
7073 
7074         assert(Date(2004, 12, 26).isoWeekYear == 2004);
7075         assert(Date(2004, 12, 27).isoWeekYear == 2004);
7076         assert(Date(2004, 12, 28).isoWeekYear == 2004);
7077         assert(Date(2004, 12, 29).isoWeekYear == 2004);
7078         assert(Date(2004, 12, 30).isoWeekYear == 2004);
7079         assert(Date(2004, 12, 31).isoWeekYear == 2004);
7080         assert(Date(2005, 1, 1).isoWeekYear == 2004);
7081         assert(Date(2005, 1, 2).isoWeekYear == 2004);
7082         assert(Date(2005, 1, 3).isoWeekYear == 2005);
7083 
7084         assert(Date(2005, 12, 31).isoWeekYear == 2005);
7085         assert(Date(2007, 1, 1).isoWeekYear == 2007);
7086 
7087         assert(Date(2007, 12, 30).isoWeekYear == 2007);
7088         assert(Date(2007, 12, 31).isoWeekYear == 2008);
7089         assert(Date(2008, 1, 1).isoWeekYear == 2008);
7090 
7091         assert(Date(2008, 12, 28).isoWeekYear == 2008);
7092         assert(Date(2008, 12, 29).isoWeekYear == 2009);
7093         assert(Date(2008, 12, 30).isoWeekYear == 2009);
7094         assert(Date(2008, 12, 31).isoWeekYear == 2009);
7095         assert(Date(2009, 1, 1).isoWeekYear == 2009);
7096         assert(Date(2009, 1, 2).isoWeekYear == 2009);
7097         assert(Date(2009, 1, 3).isoWeekYear == 2009);
7098         assert(Date(2009, 1, 4).isoWeekYear == 2009);
7099 
7100         // Test B.C.
7101         assert(Date(0, 12, 31).isoWeekYear == 0);
7102         assert(Date(0, 1, 4).isoWeekYear == 0);
7103         assert(Date(0, 1, 1).isoWeekYear == -1);
7104 
7105         const cdate = Date(1999, 7, 6);
7106         immutable idate = Date(1999, 7, 6);
7107         assert(cdate.isoWeekYear == 1999);
7108         assert(idate.isoWeekYear == 1999);
7109     }
7110 
7111     static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
7112     {
7113         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
7114         immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
7115 
7116         Date date;
7117         date._year = isoWeekYear;
7118         date._month = Month.jan;
7119         date._day = 3;
7120         immutable startOfYear = date.dayOfWeek;
7121         return date._addDays(dayOffset - startOfYear);
7122     }
7123 
7124     @safe unittest
7125     {
7126         // Test -30000 days to 30000 days for matching construction <-> deconstruction
7127         Date date = Date(1, 1, 1);
7128         date._addDays(-30_000);
7129         foreach (day; 0 .. 60_000)
7130         {
7131             const year = date.isoWeekYear;
7132             const dow = date.dayOfWeek;
7133             const isoWeek = date.isoWeek;
7134             const reversed = Date.fromISOWeek(year, isoWeek, dow);
7135             assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
7136             date = date._addDays(1);
7137         }
7138     }
7139 
7140 
7141     /++
7142         $(LREF Date) for the last day in the month that this $(LREF Date) is in.
7143       +/
7144     @property Date endOfMonth() const @safe pure nothrow
7145     {
7146         try
7147             return Date(_year, _month, maxDay(_year, _month));
7148         catch (Exception e)
7149             assert(0, "Date's constructor threw.");
7150     }
7151 
7152     ///
7153     @safe unittest
7154     {
7155         assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
7156         assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
7157         assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
7158         assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
7159     }
7160 
7161     @safe unittest
7162     {
7163         // Test A.D.
7164         assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
7165         assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
7166         assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
7167         assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
7168         assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
7169         assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
7170         assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
7171         assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
7172         assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
7173         assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
7174         assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
7175         assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
7176         assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
7177 
7178         // Test B.C.
7179         assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
7180         assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
7181         assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
7182         assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
7183         assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
7184         assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
7185         assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
7186         assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
7187         assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
7188         assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
7189         assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
7190         assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
7191         assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
7192 
7193         const cdate = Date(1999, 7, 6);
7194         immutable idate = Date(1999, 7, 6);
7195         static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
7196         static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
7197     }
7198 
7199 
7200     /++
7201         The last day in the month that this $(LREF Date) is in.
7202       +/
7203     @property ubyte daysInMonth() const @safe pure nothrow @nogc
7204     {
7205         return maxDay(_year, _month);
7206     }
7207 
7208     ///
7209     @safe unittest
7210     {
7211         assert(Date(1999, 1, 6).daysInMonth == 31);
7212         assert(Date(1999, 2, 7).daysInMonth == 28);
7213         assert(Date(2000, 2, 7).daysInMonth == 29);
7214         assert(Date(2000, 6, 4).daysInMonth == 30);
7215     }
7216 
7217     @safe unittest
7218     {
7219         // Test A.D.
7220         assert(Date(1999, 1, 1).daysInMonth == 31);
7221         assert(Date(1999, 2, 1).daysInMonth == 28);
7222         assert(Date(2000, 2, 1).daysInMonth == 29);
7223         assert(Date(1999, 3, 1).daysInMonth == 31);
7224         assert(Date(1999, 4, 1).daysInMonth == 30);
7225         assert(Date(1999, 5, 1).daysInMonth == 31);
7226         assert(Date(1999, 6, 1).daysInMonth == 30);
7227         assert(Date(1999, 7, 1).daysInMonth == 31);
7228         assert(Date(1999, 8, 1).daysInMonth == 31);
7229         assert(Date(1999, 9, 1).daysInMonth == 30);
7230         assert(Date(1999, 10, 1).daysInMonth == 31);
7231         assert(Date(1999, 11, 1).daysInMonth == 30);
7232         assert(Date(1999, 12, 1).daysInMonth == 31);
7233 
7234         // Test B.C.
7235         assert(Date(-1999, 1, 1).daysInMonth == 31);
7236         assert(Date(-1999, 2, 1).daysInMonth == 28);
7237         assert(Date(-2000, 2, 1).daysInMonth == 29);
7238         assert(Date(-1999, 3, 1).daysInMonth == 31);
7239         assert(Date(-1999, 4, 1).daysInMonth == 30);
7240         assert(Date(-1999, 5, 1).daysInMonth == 31);
7241         assert(Date(-1999, 6, 1).daysInMonth == 30);
7242         assert(Date(-1999, 7, 1).daysInMonth == 31);
7243         assert(Date(-1999, 8, 1).daysInMonth == 31);
7244         assert(Date(-1999, 9, 1).daysInMonth == 30);
7245         assert(Date(-1999, 10, 1).daysInMonth == 31);
7246         assert(Date(-1999, 11, 1).daysInMonth == 30);
7247         assert(Date(-1999, 12, 1).daysInMonth == 31);
7248 
7249         const cdate = Date(1999, 7, 6);
7250         immutable idate = Date(1999, 7, 6);
7251         static assert(!__traits(compiles, cdate.daysInMonth = 30));
7252         static assert(!__traits(compiles, idate.daysInMonth = 30));
7253     }
7254 
7255 
7256     /++
7257         Whether the current year is a date in A.D.
7258       +/
7259     @property bool isAD() const @safe pure nothrow @nogc
7260     {
7261         return _year > 0;
7262     }
7263 
7264     ///
7265     @safe unittest
7266     {
7267         assert(Date(1, 1, 1).isAD);
7268         assert(Date(2010, 12, 31).isAD);
7269         assert(!Date(0, 12, 31).isAD);
7270         assert(!Date(-2010, 1, 1).isAD);
7271     }
7272 
7273     @safe unittest
7274     {
7275         assert(Date(2010, 7, 4).isAD);
7276         assert(Date(1, 1, 1).isAD);
7277         assert(!Date(0, 1, 1).isAD);
7278         assert(!Date(-1, 1, 1).isAD);
7279         assert(!Date(-2010, 7, 4).isAD);
7280 
7281         const cdate = Date(1999, 7, 6);
7282         immutable idate = Date(1999, 7, 6);
7283         assert(cdate.isAD);
7284         assert(idate.isAD);
7285     }
7286 
7287 
7288     /++
7289         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
7290         $(LREF Date) at noon (since the Julian day changes at noon).
7291       +/
7292     @property long julianDay() const @safe pure nothrow @nogc
7293     {
7294         return dayOfGregorianCal + 1_721_425;
7295     }
7296 
7297     @safe unittest
7298     {
7299         assert(Date(-4713, 11, 24).julianDay == 0);
7300         assert(Date(0, 12, 31).julianDay == 1_721_425);
7301         assert(Date(1, 1, 1).julianDay == 1_721_426);
7302         assert(Date(1582, 10, 15).julianDay == 2_299_161);
7303         assert(Date(1858, 11, 17).julianDay == 2_400_001);
7304         assert(Date(1982, 1, 4).julianDay == 2_444_974);
7305         assert(Date(1996, 3, 31).julianDay == 2_450_174);
7306         assert(Date(2010, 8, 24).julianDay == 2_455_433);
7307 
7308         const cdate = Date(1999, 7, 6);
7309         immutable idate = Date(1999, 7, 6);
7310         assert(cdate.julianDay == 2_451_366);
7311         assert(idate.julianDay == 2_451_366);
7312     }
7313 
7314 
7315     /++
7316         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
7317         any time on this date (since, the modified Julian day changes at
7318         midnight).
7319       +/
7320     @property long modJulianDay() const @safe pure nothrow @nogc
7321     {
7322         return julianDay - 2_400_001;
7323     }
7324 
7325     @safe unittest
7326     {
7327         assert(Date(1858, 11, 17).modJulianDay == 0);
7328         assert(Date(2010, 8, 24).modJulianDay == 55_432);
7329 
7330         const cdate = Date(1999, 7, 6);
7331         immutable idate = Date(1999, 7, 6);
7332         assert(cdate.modJulianDay == 51_365);
7333         assert(idate.modJulianDay == 51_365);
7334     }
7335 
7336 
7337     /++
7338         Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
7339         If `writer` is set, the resulting string will be written directly
7340         to it.
7341 
7342         Params:
7343             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7344         Returns:
7345             A `string` when not using an output range; `void` otherwise.
7346       +/
7347     string toISOString() const @safe pure nothrow
7348     {
7349         import std.array : appender;
7350         auto w = appender!string();
7351         w.reserve(8);
7352         try
7353             toISOString(w);
7354         catch (Exception e)
7355             assert(0, "toISOString() threw.");
7356         return w.data;
7357     }
7358 
7359     ///
7360     @safe unittest
7361     {
7362         assert(Date(2010, 7, 4).toISOString() == "20100704");
7363         assert(Date(1998, 12, 25).toISOString() == "19981225");
7364         assert(Date(0, 1, 5).toISOString() == "00000105");
7365         assert(Date(-4, 1, 5).toISOString() == "-00040105");
7366     }
7367 
7368     @safe unittest
7369     {
7370         // Test A.D.
7371         assert(Date(9, 12, 4).toISOString() == "00091204");
7372         assert(Date(99, 12, 4).toISOString() == "00991204");
7373         assert(Date(999, 12, 4).toISOString() == "09991204");
7374         assert(Date(9999, 7, 4).toISOString() == "99990704");
7375         assert(Date(10000, 10, 20).toISOString() == "+100001020");
7376 
7377         // Test B.C.
7378         assert(Date(0, 12, 4).toISOString() == "00001204");
7379         assert(Date(-9, 12, 4).toISOString() == "-00091204");
7380         assert(Date(-99, 12, 4).toISOString() == "-00991204");
7381         assert(Date(-999, 12, 4).toISOString() == "-09991204");
7382         assert(Date(-9999, 7, 4).toISOString() == "-99990704");
7383         assert(Date(-10000, 10, 20).toISOString() == "-100001020");
7384 
7385         const cdate = Date(1999, 7, 6);
7386         immutable idate = Date(1999, 7, 6);
7387         assert(cdate.toISOString() == "19990706");
7388         assert(idate.toISOString() == "19990706");
7389     }
7390 
7391     /// ditto
7392     void toISOString(W)(ref W writer) const
7393     if (isOutputRange!(W, char))
7394     {
7395         import std.format.write : formattedWrite;
7396         if (_year >= 0)
7397         {
7398             if (_year < 10_000)
7399                 formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
7400             else
7401                 formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
7402         }
7403         else if (_year > -10_000)
7404             formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
7405         else
7406             formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
7407     }
7408 
7409     @safe pure unittest
7410     {
7411         import std.array : appender;
7412 
7413         auto w = appender!(char[])();
7414         Date(2010, 7, 4).toISOString(w);
7415         assert(w.data == "20100704");
7416         w.clear();
7417         Date(1998, 12, 25).toISOString(w);
7418         assert(w.data == "19981225");
7419     }
7420 
7421     /++
7422         Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
7423         If `writer` is set, the resulting string will be written directly
7424         to it.
7425 
7426         Params:
7427             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7428         Returns:
7429             A `string` when not using an output range; `void` otherwise.
7430       +/
7431     string toISOExtString() const @safe pure nothrow
7432     {
7433         import std.array : appender;
7434         auto w = appender!string();
7435         w.reserve(10);
7436         try
7437             toISOExtString(w);
7438         catch (Exception e)
7439             assert(0, "toISOExtString() threw.");
7440         return w.data;
7441     }
7442 
7443     ///
7444     @safe unittest
7445     {
7446         assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
7447         assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
7448         assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
7449         assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
7450     }
7451 
7452     @safe unittest
7453     {
7454         // Test A.D.
7455         assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
7456         assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
7457         assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
7458         assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
7459         assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
7460 
7461         // Test B.C.
7462         assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
7463         assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
7464         assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
7465         assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
7466         assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
7467         assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
7468 
7469         const cdate = Date(1999, 7, 6);
7470         immutable idate = Date(1999, 7, 6);
7471         assert(cdate.toISOExtString() == "1999-07-06");
7472         assert(idate.toISOExtString() == "1999-07-06");
7473     }
7474 
7475     /// ditto
7476     void toISOExtString(W)(ref W writer) const
7477     if (isOutputRange!(W, char))
7478     {
7479         import std.format.write : formattedWrite;
7480         if (_year >= 0)
7481         {
7482             if (_year < 10_000)
7483                 formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
7484             else
7485                 formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
7486         }
7487         else if (_year > -10_000)
7488             formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
7489         else
7490             formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
7491     }
7492 
7493     @safe pure unittest
7494     {
7495         import std.array : appender;
7496 
7497         auto w = appender!(char[])();
7498         Date(2010, 7, 4).toISOExtString(w);
7499         assert(w.data == "2010-07-04");
7500         w.clear();
7501         Date(-4, 1, 5).toISOExtString(w);
7502         assert(w.data == "-0004-01-05");
7503     }
7504 
7505     /++
7506         Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
7507         If `writer` is set, the resulting string will be written directly
7508         to it.
7509 
7510         Params:
7511             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7512         Returns:
7513             A `string` when not using an output range; `void` otherwise.
7514       +/
7515     string toSimpleString() const @safe pure nothrow
7516     {
7517         import std.array : appender;
7518         auto w = appender!string();
7519         w.reserve(11);
7520         try
7521             toSimpleString(w);
7522         catch (Exception e)
7523             assert(0, "toSimpleString() threw.");
7524         return w.data;
7525     }
7526 
7527     ///
7528     @safe unittest
7529     {
7530         assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
7531         assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
7532         assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
7533         assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
7534     }
7535 
7536     @safe unittest
7537     {
7538         // Test A.D.
7539         assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
7540         assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
7541         assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
7542         assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
7543         assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
7544 
7545         // Test B.C.
7546         assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
7547         assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
7548         assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
7549         assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
7550         assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
7551         assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
7552 
7553         const cdate = Date(1999, 7, 6);
7554         immutable idate = Date(1999, 7, 6);
7555         assert(cdate.toSimpleString() == "1999-Jul-06");
7556         assert(idate.toSimpleString() == "1999-Jul-06");
7557     }
7558 
7559     /// ditto
7560     void toSimpleString(W)(ref W writer) const
7561     if (isOutputRange!(W, char))
7562     {
7563         import std.format.write : formattedWrite;
7564         if (_year >= 0)
7565         {
7566             if (_year < 10_000)
7567                 formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
7568             else
7569                 formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
7570         }
7571         else if (_year > -10_000)
7572             formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
7573         else
7574             formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
7575     }
7576 
7577     @safe pure unittest
7578     {
7579         import std.array : appender;
7580 
7581         auto w = appender!(char[])();
7582         Date(9, 12, 4).toSimpleString(w);
7583         assert(w.data == "0009-Dec-04");
7584         w.clear();
7585         Date(-10000, 10, 20).toSimpleString(w);
7586         assert(w.data == "-10000-Oct-20");
7587     }
7588 
7589     /++
7590         Converts this $(LREF Date) to a string.
7591 
7592         This function exists to make it easy to convert a $(LREF Date) to a
7593         string for code that does not care what the exact format is - just that
7594         it presents the information in a clear manner. It also makes it easy to
7595         simply convert a $(LREF Date) to a string when using functions such as
7596         `to!string`, `format`, or `writeln` which use toString to convert
7597         user-defined types. So, it is unlikely that much code will call
7598         toString directly.
7599 
7600         The format of the string is purposefully unspecified, and code that
7601         cares about the format of the string should use `toISOString`,
7602         `toISOExtString`, `toSimpleString`, or some other custom formatting
7603         function that explicitly generates the format that the code needs. The
7604         reason is that the code is then clear about what format it's using,
7605         making it less error-prone to maintain the code and interact with other
7606         software that consumes the generated strings. It's for this same reason
7607         $(LREF Date) has no `fromString` function, whereas it does have
7608         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
7609 
7610         The format returned by toString may or may not change in the future.
7611       +/
7612     string toString() const @safe pure nothrow
7613     {
7614         return toSimpleString();
7615     }
7616 
7617     @safe unittest
7618     {
7619         auto date = Date(1999, 7, 6);
7620         const cdate = Date(1999, 7, 6);
7621         immutable idate = Date(1999, 7, 6);
7622         assert(date.toString());
7623         assert(cdate.toString());
7624         assert(idate.toString());
7625     }
7626 
7627     /// ditto
7628     void toString(W)(ref W writer) const
7629     if (isOutputRange!(W, char))
7630     {
7631         toSimpleString(writer);
7632     }
7633 
7634     /++
7635         Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
7636         is stripped from the given string.
7637 
7638         Params:
7639             isoString = A string formatted in the ISO format for dates.
7640 
7641         Throws:
7642             $(REF DateTimeException,std,datetime,date) if the given string is
7643             not in the ISO format or if the resulting $(LREF Date) would not be
7644             valid.
7645       +/
7646     static Date fromISOString(S)(scope const S isoString) @safe pure
7647     if (isSomeString!S)
7648     {
7649         import std.algorithm.searching : startsWith;
7650         import std.conv : to, text, ConvException;
7651         import std.exception : enforce;
7652         import std.string : strip;
7653 
7654         auto str = isoString.strip;
7655 
7656         enforce!DateTimeException(str.length >= 8, text("Invalid format for Date.fromISOString: ", isoString));
7657 
7658         int day, month, year;
7659         auto yearStr = str[0 .. $ - 4];
7660 
7661         try
7662         {
7663             // using conversion to uint plus cast because it checks for +/-
7664             // for us quickly while throwing ConvException
7665             day = cast(int) to!uint(str[$ - 2 .. $]);
7666             month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
7667 
7668             if (yearStr.length > 4)
7669             {
7670                 enforce!DateTimeException(yearStr.startsWith('-', '+'),
7671                         text("Invalid format for Date.fromISOString: ", isoString));
7672                 year = to!int(yearStr);
7673             }
7674             else
7675             {
7676                 year = cast(int) to!uint(yearStr);
7677             }
7678         }
7679         catch (ConvException)
7680         {
7681             throw new DateTimeException(text("Invalid format for Date.fromISOString: ", isoString));
7682         }
7683 
7684         return Date(year, month, day);
7685     }
7686 
7687     ///
7688     @safe unittest
7689     {
7690         assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
7691         assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
7692         assert(Date.fromISOString("00000105") == Date(0, 1, 5));
7693         assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
7694         assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
7695     }
7696 
7697     @safe unittest
7698     {
7699         assertThrown!DateTimeException(Date.fromISOString(""));
7700         assertThrown!DateTimeException(Date.fromISOString("990704"));
7701         assertThrown!DateTimeException(Date.fromISOString("0100704"));
7702         assertThrown!DateTimeException(Date.fromISOString("2010070"));
7703         assertThrown!DateTimeException(Date.fromISOString("2010070 "));
7704         assertThrown!DateTimeException(Date.fromISOString("120100704"));
7705         assertThrown!DateTimeException(Date.fromISOString("-0100704"));
7706         assertThrown!DateTimeException(Date.fromISOString("+0100704"));
7707         assertThrown!DateTimeException(Date.fromISOString("2010070a"));
7708         assertThrown!DateTimeException(Date.fromISOString("20100a04"));
7709         assertThrown!DateTimeException(Date.fromISOString("2010a704"));
7710 
7711         assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
7712         assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
7713         assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
7714         assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
7715         assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
7716         assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
7717         assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
7718         assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
7719         assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
7720         assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
7721         assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
7722         assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
7723         assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
7724         assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
7725         assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
7726         assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
7727         assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
7728 
7729         assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
7730         assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
7731         assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
7732         assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
7733         assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
7734         assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
7735         assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
7736         assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
7737         assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
7738         assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
7739 
7740         assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
7741         assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
7742         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
7743         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
7744         assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
7745         assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
7746         assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
7747         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
7748         assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
7749         assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
7750         assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
7751 
7752         assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
7753         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
7754 
7755         assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
7756         assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
7757         assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
7758         assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
7759         assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
7760         assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
7761     }
7762 
7763     // https://issues.dlang.org/show_bug.cgi?id=17801
7764     @safe unittest
7765     {
7766         import std.conv : to;
7767         import std.meta : AliasSeq;
7768         static foreach (C; AliasSeq!(char, wchar, dchar))
7769         {
7770             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7771                 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
7772         }
7773     }
7774 
7775 
7776     /++
7777         Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
7778         Whitespace is stripped from the given string.
7779 
7780         Params:
7781             isoExtString = A string formatted in the ISO Extended format for
7782                            dates.
7783 
7784         Throws:
7785             $(REF DateTimeException,std,datetime,date) if the given string is
7786             not in the ISO Extended format or if the resulting $(LREF Date)
7787             would not be valid.
7788       +/
7789     static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
7790     if (isSomeString!(S))
7791     {
7792         import std.algorithm.searching : startsWith;
7793         import std.conv : to, ConvException;
7794         import std.format : format;
7795         import std.string : strip;
7796 
7797         auto str = strip(isoExtString);
7798         short year;
7799         ubyte month, day;
7800 
7801         if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
7802             throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7803 
7804         auto yearStr = str[0 .. $-6];
7805         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7806         if ((yearStr.length > 4) != signAtBegining)
7807         {
7808             throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7809         }
7810 
7811         try
7812         {
7813             day = to!ubyte(str[$-2 .. $]);
7814             month = to!ubyte(str[$-5 .. $-3]);
7815             year = to!short(yearStr);
7816         }
7817         catch (ConvException)
7818         {
7819             throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7820         }
7821 
7822         return Date(year, month, day);
7823     }
7824 
7825     ///
7826     @safe unittest
7827     {
7828         assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
7829         assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
7830         assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
7831         assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
7832         assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
7833     }
7834 
7835     @safe unittest
7836     {
7837         assertThrown!DateTimeException(Date.fromISOExtString(""));
7838         assertThrown!DateTimeException(Date.fromISOExtString("990704"));
7839         assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
7840         assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
7841         assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
7842         assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
7843         assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
7844         assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
7845         assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
7846         assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
7847         assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
7848 
7849         assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
7850         assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
7851         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
7852         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
7853         assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
7854         assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
7855         assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
7856         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
7857         assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
7858         assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
7859         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
7860         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
7861         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
7862         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
7863         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
7864         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
7865         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
7866 
7867         assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
7868         assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
7869         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
7870         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
7871         assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
7872         assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
7873         assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
7874         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
7875         assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
7876         assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
7877 
7878         assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
7879         assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
7880         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
7881         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
7882         assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
7883         assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
7884         assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
7885         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
7886         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
7887         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
7888         assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
7889 
7890         assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
7891         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
7892 
7893         assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
7894         assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
7895         assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
7896         assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
7897         assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
7898         assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
7899     }
7900 
7901     // https://issues.dlang.org/show_bug.cgi?id=17801
7902     @safe unittest
7903     {
7904         import std.conv : to;
7905         import std.meta : AliasSeq;
7906         static foreach (C; AliasSeq!(char, wchar, dchar))
7907         {
7908             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7909                 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
7910         }
7911     }
7912 
7913 
7914     /++
7915         Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
7916         Whitespace is stripped from the given string.
7917 
7918         Params:
7919             simpleString = A string formatted in the way that toSimpleString
7920                            formats dates.
7921 
7922         Throws:
7923             $(REF DateTimeException,std,datetime,date) if the given string is
7924             not in the correct format or if the resulting $(LREF Date) would not
7925             be valid.
7926       +/
7927     static Date fromSimpleString(S)(scope const S simpleString) @safe pure
7928     if (isSomeString!(S))
7929     {
7930         import std.algorithm.searching : startsWith;
7931         import std.conv : to, ConvException;
7932         import std.format : format;
7933         import std.string : strip;
7934 
7935         auto str = strip(simpleString);
7936 
7937         if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
7938             throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7939 
7940         int year;
7941         uint day;
7942         auto month = monthFromString(str[$ - 6 .. $ - 3]);
7943         auto yearStr = str[0 .. $ - 7];
7944         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7945         if ((yearStr.length > 4) != signAtBegining)
7946         {
7947             throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7948         }
7949 
7950         try
7951         {
7952             day = to!uint(str[$ - 2 .. $]);
7953             year = to!int(yearStr);
7954         }
7955         catch (ConvException)
7956         {
7957             throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7958         }
7959 
7960         return Date(year, month, day);
7961     }
7962 
7963     ///
7964     @safe unittest
7965     {
7966         assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
7967         assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
7968         assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
7969         assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
7970         assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
7971     }
7972 
7973     @safe unittest
7974     {
7975         assertThrown!DateTimeException(Date.fromSimpleString(""));
7976         assertThrown!DateTimeException(Date.fromSimpleString("990704"));
7977         assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
7978         assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
7979         assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
7980         assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
7981         assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
7982         assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
7983         assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
7984         assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
7985         assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
7986 
7987         assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
7988         assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
7989         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
7990         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
7991         assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
7992         assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
7993         assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
7994         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
7995         assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
7996         assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
7997         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
7998         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
7999         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
8000         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
8001         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
8002         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
8003         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
8004 
8005         assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
8006         assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
8007         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
8008         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
8009         assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
8010         assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
8011         assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
8012         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
8013         assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
8014         assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
8015 
8016         assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
8017         assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
8018         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
8019         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
8020         assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
8021         assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
8022         assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
8023         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
8024         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
8025         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
8026         assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
8027 
8028         assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
8029         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
8030 
8031         assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
8032         assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
8033         assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
8034         assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
8035         assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
8036         assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
8037     }
8038 
8039     // https://issues.dlang.org/show_bug.cgi?id=17801
8040     @safe unittest
8041     {
8042         import std.conv : to;
8043         import std.meta : AliasSeq;
8044         static foreach (C; AliasSeq!(char, wchar, dchar))
8045         {
8046             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
8047                 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
8048         }
8049     }
8050 
8051 
8052     /++
8053         Returns the $(LREF Date) farthest in the past which is representable by
8054         $(LREF Date).
8055       +/
8056     @property static Date min() @safe pure nothrow @nogc
8057     {
8058         auto date = Date.init;
8059         date._year = short.min;
8060         date._month = Month.jan;
8061         date._day = 1;
8062 
8063         return date;
8064     }
8065 
8066     @safe unittest
8067     {
8068         assert(Date.min.year < 0);
8069         assert(Date.min < Date.max);
8070     }
8071 
8072 
8073     /++
8074         Returns the $(LREF Date) farthest in the future which is representable
8075         by $(LREF Date).
8076       +/
8077     @property static Date max() @safe pure nothrow @nogc
8078     {
8079         auto date = Date.init;
8080         date._year = short.max;
8081         date._month = Month.dec;
8082         date._day = 31;
8083 
8084         return date;
8085     }
8086 
8087     @safe unittest
8088     {
8089         assert(Date.max.year > 0);
8090         assert(Date.max > Date.min);
8091     }
8092 
8093 
8094 private:
8095 
8096     /+
8097         Whether the given values form a valid date.
8098 
8099         Params:
8100             year  = The year to test.
8101             month = The month of the Gregorian Calendar to test.
8102             day   = The day of the month to test.
8103      +/
8104     static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
8105     {
8106         if (!valid!"months"(month))
8107             return false;
8108         return valid!"days"(year, month, day);
8109     }
8110 
8111 
8112 package:
8113 
8114     /+
8115         Adds the given number of days to this $(LREF Date). A negative number
8116         will subtract.
8117 
8118         The month will be adjusted along with the day if the number of days
8119         added (or subtracted) would overflow (or underflow) the current month.
8120         The year will be adjusted along with the month if the increase (or
8121         decrease) to the month would cause it to overflow (or underflow) the
8122         current year.
8123 
8124         `_addDays(numDays)` is effectively equivalent to
8125         $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
8126 
8127         Params:
8128             days = The number of days to add to this Date.
8129       +/
8130     ref Date _addDays(long days) return @safe pure nothrow @nogc
8131     {
8132         dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
8133         return this;
8134     }
8135 
8136     @safe unittest
8137     {
8138         // Test A.D.
8139         {
8140             auto date = Date(1999, 2, 28);
8141             date._addDays(1);
8142             assert(date == Date(1999, 3, 1));
8143             date._addDays(-1);
8144             assert(date == Date(1999, 2, 28));
8145         }
8146 
8147         {
8148             auto date = Date(2000, 2, 28);
8149             date._addDays(1);
8150             assert(date == Date(2000, 2, 29));
8151             date._addDays(1);
8152             assert(date == Date(2000, 3, 1));
8153             date._addDays(-1);
8154             assert(date == Date(2000, 2, 29));
8155         }
8156 
8157         {
8158             auto date = Date(1999, 6, 30);
8159             date._addDays(1);
8160             assert(date == Date(1999, 7, 1));
8161             date._addDays(-1);
8162             assert(date == Date(1999, 6, 30));
8163         }
8164 
8165         {
8166             auto date = Date(1999, 7, 31);
8167             date._addDays(1);
8168             assert(date == Date(1999, 8, 1));
8169             date._addDays(-1);
8170             assert(date == Date(1999, 7, 31));
8171         }
8172 
8173         {
8174             auto date = Date(1999, 1, 1);
8175             date._addDays(-1);
8176             assert(date == Date(1998, 12, 31));
8177             date._addDays(1);
8178             assert(date == Date(1999, 1, 1));
8179         }
8180 
8181         {
8182             auto date = Date(1999, 7, 6);
8183             date._addDays(9);
8184             assert(date == Date(1999, 7, 15));
8185             date._addDays(-11);
8186             assert(date == Date(1999, 7, 4));
8187             date._addDays(30);
8188             assert(date == Date(1999, 8, 3));
8189             date._addDays(-3);
8190             assert(date == Date(1999, 7, 31));
8191         }
8192 
8193         {
8194             auto date = Date(1999, 7, 6);
8195             date._addDays(365);
8196             assert(date == Date(2000, 7, 5));
8197             date._addDays(-365);
8198             assert(date == Date(1999, 7, 6));
8199             date._addDays(366);
8200             assert(date == Date(2000, 7, 6));
8201             date._addDays(730);
8202             assert(date == Date(2002, 7, 6));
8203             date._addDays(-1096);
8204             assert(date == Date(1999, 7, 6));
8205         }
8206 
8207         // Test B.C.
8208         {
8209             auto date = Date(-1999, 2, 28);
8210             date._addDays(1);
8211             assert(date == Date(-1999, 3, 1));
8212             date._addDays(-1);
8213             assert(date == Date(-1999, 2, 28));
8214         }
8215 
8216         {
8217             auto date = Date(-2000, 2, 28);
8218             date._addDays(1);
8219             assert(date == Date(-2000, 2, 29));
8220             date._addDays(1);
8221             assert(date == Date(-2000, 3, 1));
8222             date._addDays(-1);
8223             assert(date == Date(-2000, 2, 29));
8224         }
8225 
8226         {
8227             auto date = Date(-1999, 6, 30);
8228             date._addDays(1);
8229             assert(date == Date(-1999, 7, 1));
8230             date._addDays(-1);
8231             assert(date == Date(-1999, 6, 30));
8232         }
8233 
8234         {
8235             auto date = Date(-1999, 7, 31);
8236             date._addDays(1);
8237             assert(date == Date(-1999, 8, 1));
8238             date._addDays(-1);
8239             assert(date == Date(-1999, 7, 31));
8240         }
8241 
8242         {
8243             auto date = Date(-1999, 1, 1);
8244             date._addDays(-1);
8245             assert(date == Date(-2000, 12, 31));
8246             date._addDays(1);
8247             assert(date == Date(-1999, 1, 1));
8248         }
8249 
8250         {
8251             auto date = Date(-1999, 7, 6);
8252             date._addDays(9);
8253             assert(date == Date(-1999, 7, 15));
8254             date._addDays(-11);
8255             assert(date == Date(-1999, 7, 4));
8256             date._addDays(30);
8257             assert(date == Date(-1999, 8, 3));
8258             date._addDays(-3);
8259         }
8260 
8261         {
8262             auto date = Date(-1999, 7, 6);
8263             date._addDays(365);
8264             assert(date == Date(-1998, 7, 6));
8265             date._addDays(-365);
8266             assert(date == Date(-1999, 7, 6));
8267             date._addDays(366);
8268             assert(date == Date(-1998, 7, 7));
8269             date._addDays(730);
8270             assert(date == Date(-1996, 7, 6));
8271             date._addDays(-1096);
8272             assert(date == Date(-1999, 7, 6));
8273         }
8274 
8275         // Test Both
8276         {
8277             auto date = Date(1, 7, 6);
8278             date._addDays(-365);
8279             assert(date == Date(0, 7, 6));
8280             date._addDays(365);
8281             assert(date == Date(1, 7, 6));
8282             date._addDays(-731);
8283             assert(date == Date(-1, 7, 6));
8284             date._addDays(730);
8285             assert(date == Date(1, 7, 5));
8286         }
8287 
8288         const cdate = Date(1999, 7, 6);
8289         immutable idate = Date(1999, 7, 6);
8290         static assert(!__traits(compiles, cdate._addDays(12)));
8291         static assert(!__traits(compiles, idate._addDays(12)));
8292     }
8293 
8294 
8295     @safe pure invariant()
8296     {
8297         import std.format : format;
8298         assert(valid!"months"(_month),
8299                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8300         assert(valid!"days"(_year, _month, _day),
8301                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8302     }
8303 
8304     short _year  = 1;
8305     Month _month = Month.jan;
8306     ubyte _day   = 1;
8307 }
8308 
8309 ///
8310 @safe pure unittest
8311 {
8312     import core.time : days;
8313 
8314     auto d = Date(2000, 6, 1);
8315 
8316     assert(d.dayOfYear == 153);
8317     assert(d.dayOfWeek == DayOfWeek.thu);
8318 
8319     d += 10.days;
8320     assert(d == Date(2000, 6, 11));
8321 
8322     assert(d.toISOExtString() == "2000-06-11");
8323     assert(d.toISOString() == "20000611");
8324     assert(d.toSimpleString() == "2000-Jun-11");
8325 
8326     assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
8327     assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
8328     assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
8329 }
8330 
8331 
8332 /++
8333     Represents a time of day with hours, minutes, and seconds. It uses 24 hour
8334     time.
8335 +/
8336 struct TimeOfDay
8337 {
8338 public:
8339 
8340     /++
8341         Params:
8342             hour   = Hour of the day [0 - 24$(RPAREN).
8343             minute = Minute of the hour [0 - 60$(RPAREN).
8344             second = Second of the minute [0 - 60$(RPAREN).
8345 
8346         Throws:
8347             $(REF DateTimeException,std,datetime,date) if the resulting
8348             $(LREF TimeOfDay) would be not be valid.
8349      +/
8350     this(int hour, int minute, int second = 0) @safe pure
8351     {
8352         enforceValid!"hours"(hour);
8353         enforceValid!"minutes"(minute);
8354         enforceValid!"seconds"(second);
8355 
8356         _hour   = cast(ubyte) hour;
8357         _minute = cast(ubyte) minute;
8358         _second = cast(ubyte) second;
8359     }
8360 
8361     @safe unittest
8362     {
8363         assert(TimeOfDay(0, 0) == TimeOfDay.init);
8364 
8365         {
8366             auto tod = TimeOfDay(0, 0);
8367             assert(tod._hour == 0);
8368             assert(tod._minute == 0);
8369             assert(tod._second == 0);
8370         }
8371 
8372         {
8373             auto tod = TimeOfDay(12, 30, 33);
8374             assert(tod._hour == 12);
8375             assert(tod._minute == 30);
8376             assert(tod._second == 33);
8377         }
8378 
8379         {
8380             auto tod = TimeOfDay(23, 59, 59);
8381             assert(tod._hour == 23);
8382             assert(tod._minute == 59);
8383             assert(tod._second == 59);
8384         }
8385 
8386         assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
8387         assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
8388         assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
8389     }
8390 
8391 
8392     /++
8393         Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
8394 
8395         Returns:
8396             $(BOOKTABLE,
8397             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
8398             $(TR $(TD this == rhs) $(TD 0))
8399             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
8400             )
8401      +/
8402     int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
8403     {
8404         if (_hour < rhs._hour)
8405             return -1;
8406         if (_hour > rhs._hour)
8407             return 1;
8408 
8409         if (_minute < rhs._minute)
8410             return -1;
8411         if (_minute > rhs._minute)
8412             return 1;
8413 
8414         if (_second < rhs._second)
8415             return -1;
8416         if (_second > rhs._second)
8417             return 1;
8418 
8419         return 0;
8420     }
8421 
8422     @safe unittest
8423     {
8424         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
8425 
8426         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
8427         assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
8428         assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
8429         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8430 
8431         assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
8432         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
8433 
8434         assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
8435         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8436 
8437         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8438         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8439         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
8440         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8441         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
8442         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
8443 
8444         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8445         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
8446         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
8447         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8448 
8449         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8450         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
8451 
8452         const ctod = TimeOfDay(12, 30, 33);
8453         immutable itod = TimeOfDay(12, 30, 33);
8454         assert(ctod.opCmp(itod) == 0);
8455         assert(itod.opCmp(ctod) == 0);
8456     }
8457 
8458 
8459     /++
8460         Hours past midnight.
8461      +/
8462     @property ubyte hour() const @safe pure nothrow @nogc
8463     {
8464         return _hour;
8465     }
8466 
8467     @safe unittest
8468     {
8469         assert(TimeOfDay.init.hour == 0);
8470         assert(TimeOfDay(12, 0, 0).hour == 12);
8471 
8472         const ctod = TimeOfDay(12, 0, 0);
8473         immutable itod = TimeOfDay(12, 0, 0);
8474         assert(ctod.hour == 12);
8475         assert(itod.hour == 12);
8476     }
8477 
8478 
8479     /++
8480         Hours past midnight.
8481 
8482         Params:
8483             hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
8484 
8485         Throws:
8486             $(REF DateTimeException,std,datetime,date) if the given hour would
8487             result in an invalid $(LREF TimeOfDay).
8488      +/
8489     @property void hour(int hour) @safe pure
8490     {
8491         enforceValid!"hours"(hour);
8492         _hour = cast(ubyte) hour;
8493     }
8494 
8495     @safe unittest
8496     {
8497         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
8498 
8499         auto tod = TimeOfDay(0, 0, 0);
8500         tod.hour = 12;
8501         assert(tod == TimeOfDay(12, 0, 0));
8502 
8503         const ctod = TimeOfDay(0, 0, 0);
8504         immutable itod = TimeOfDay(0, 0, 0);
8505         static assert(!__traits(compiles, ctod.hour = 12));
8506         static assert(!__traits(compiles, itod.hour = 12));
8507     }
8508 
8509 
8510     /++
8511         Minutes past the hour.
8512      +/
8513     @property ubyte minute() const @safe pure nothrow @nogc
8514     {
8515         return _minute;
8516     }
8517 
8518     @safe unittest
8519     {
8520         assert(TimeOfDay.init.minute == 0);
8521         assert(TimeOfDay(0, 30, 0).minute == 30);
8522 
8523         const ctod = TimeOfDay(0, 30, 0);
8524         immutable itod = TimeOfDay(0, 30, 0);
8525         assert(ctod.minute == 30);
8526         assert(itod.minute == 30);
8527     }
8528 
8529 
8530     /++
8531         Minutes past the hour.
8532 
8533         Params:
8534             minute = The minute to set this $(LREF TimeOfDay)'s minute to.
8535 
8536         Throws:
8537             $(REF DateTimeException,std,datetime,date) if the given minute
8538             would result in an invalid $(LREF TimeOfDay).
8539      +/
8540     @property void minute(int minute) @safe pure
8541     {
8542         enforceValid!"minutes"(minute);
8543         _minute = cast(ubyte) minute;
8544     }
8545 
8546     @safe unittest
8547     {
8548         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
8549 
8550         auto tod = TimeOfDay(0, 0, 0);
8551         tod.minute = 30;
8552         assert(tod == TimeOfDay(0, 30, 0));
8553 
8554         const ctod = TimeOfDay(0, 0, 0);
8555         immutable itod = TimeOfDay(0, 0, 0);
8556         static assert(!__traits(compiles, ctod.minute = 30));
8557         static assert(!__traits(compiles, itod.minute = 30));
8558     }
8559 
8560 
8561     /++
8562         Seconds past the minute.
8563      +/
8564     @property ubyte second() const @safe pure nothrow @nogc
8565     {
8566         return _second;
8567     }
8568 
8569     @safe unittest
8570     {
8571         assert(TimeOfDay.init.second == 0);
8572         assert(TimeOfDay(0, 0, 33).second == 33);
8573 
8574         const ctod = TimeOfDay(0, 0, 33);
8575         immutable itod = TimeOfDay(0, 0, 33);
8576         assert(ctod.second == 33);
8577         assert(itod.second == 33);
8578     }
8579 
8580 
8581     /++
8582         Seconds past the minute.
8583 
8584         Params:
8585             second = The second to set this $(LREF TimeOfDay)'s second to.
8586 
8587         Throws:
8588             $(REF DateTimeException,std,datetime,date) if the given second
8589             would result in an invalid $(LREF TimeOfDay).
8590      +/
8591     @property void second(int second) @safe pure
8592     {
8593         enforceValid!"seconds"(second);
8594         _second = cast(ubyte) second;
8595     }
8596 
8597     @safe unittest
8598     {
8599         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
8600 
8601         auto tod = TimeOfDay(0, 0, 0);
8602         tod.second = 33;
8603         assert(tod == TimeOfDay(0, 0, 33));
8604 
8605         const ctod = TimeOfDay(0, 0, 0);
8606         immutable itod = TimeOfDay(0, 0, 0);
8607         static assert(!__traits(compiles, ctod.second = 33));
8608         static assert(!__traits(compiles, itod.second = 33));
8609     }
8610 
8611 
8612     /++
8613         Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
8614         negative number will subtract.
8615 
8616         The difference between rolling and adding is that rolling does not
8617         affect larger units. For instance, rolling a $(LREF TimeOfDay)
8618         one hours's worth of minutes gets the exact same
8619         $(LREF TimeOfDay).
8620 
8621         Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
8622 
8623         Params:
8624             units = The units to add.
8625             value = The number of $(D_PARAM units) to add to this
8626                     $(LREF TimeOfDay).
8627 
8628         Returns:
8629             A reference to the `TimeOfDay` (`this`).
8630       +/
8631     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8632     if (units == "hours")
8633     {
8634         import core.time : dur;
8635         return this += dur!"hours"(value);
8636     }
8637 
8638     ///
8639     @safe unittest
8640     {
8641         auto tod1 = TimeOfDay(7, 12, 0);
8642         tod1.roll!"hours"(1);
8643         assert(tod1 == TimeOfDay(8, 12, 0));
8644 
8645         auto tod2 = TimeOfDay(7, 12, 0);
8646         tod2.roll!"hours"(-1);
8647         assert(tod2 == TimeOfDay(6, 12, 0));
8648 
8649         auto tod3 = TimeOfDay(23, 59, 0);
8650         tod3.roll!"minutes"(1);
8651         assert(tod3 == TimeOfDay(23, 0, 0));
8652 
8653         auto tod4 = TimeOfDay(0, 0, 0);
8654         tod4.roll!"minutes"(-1);
8655         assert(tod4 == TimeOfDay(0, 59, 0));
8656 
8657         auto tod5 = TimeOfDay(23, 59, 59);
8658         tod5.roll!"seconds"(1);
8659         assert(tod5 == TimeOfDay(23, 59, 0));
8660 
8661         auto tod6 = TimeOfDay(0, 0, 0);
8662         tod6.roll!"seconds"(-1);
8663         assert(tod6 == TimeOfDay(0, 0, 59));
8664     }
8665 
8666     @safe unittest
8667     {
8668         auto tod = TimeOfDay(12, 27, 2);
8669         tod.roll!"hours"(22).roll!"hours"(-7);
8670         assert(tod == TimeOfDay(3, 27, 2));
8671 
8672         const ctod = TimeOfDay(0, 0, 0);
8673         immutable itod = TimeOfDay(0, 0, 0);
8674         static assert(!__traits(compiles, ctod.roll!"hours"(53)));
8675         static assert(!__traits(compiles, itod.roll!"hours"(53)));
8676     }
8677 
8678 
8679     /// ditto
8680     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8681     if (units == "minutes" || units == "seconds")
8682     {
8683         import std.format : format;
8684 
8685         enum memberVarStr = units[0 .. $ - 1];
8686         value %= 60;
8687         mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
8688 
8689         if (value < 0)
8690         {
8691             if (newVal < 0)
8692                 newVal += 60;
8693         }
8694         else if (newVal >= 60)
8695             newVal -= 60;
8696 
8697         mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
8698         return this;
8699     }
8700 
8701     // Test roll!"minutes"().
8702     @safe unittest
8703     {
8704         static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
8705         {
8706             orig.roll!"minutes"(minutes);
8707             assert(orig == expected);
8708         }
8709 
8710         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8711         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
8712         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
8713         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
8714         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
8715         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
8716         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
8717         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
8718         testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
8719         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
8720         testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
8721         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8722         testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
8723         testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
8724         testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
8725 
8726         testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
8727         testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
8728         testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
8729         testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
8730         testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
8731         testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
8732         testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
8733         testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
8734 
8735         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
8736         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
8737         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
8738         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
8739         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
8740         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
8741         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
8742         testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
8743         testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
8744         testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
8745         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8746         testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
8747         testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
8748         testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
8749 
8750         testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
8751         testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
8752         testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
8753         testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
8754         testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
8755         testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
8756         testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
8757         testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
8758 
8759         testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
8760         testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
8761         testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
8762 
8763         testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
8764         testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
8765         testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
8766 
8767         testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
8768         testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
8769         testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
8770 
8771         testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
8772         testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
8773         testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
8774 
8775         auto tod = TimeOfDay(12, 27, 2);
8776         tod.roll!"minutes"(97).roll!"minutes"(-102);
8777         assert(tod == TimeOfDay(12, 22, 2));
8778 
8779         const ctod = TimeOfDay(0, 0, 0);
8780         immutable itod = TimeOfDay(0, 0, 0);
8781         static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
8782         static assert(!__traits(compiles, itod.roll!"minutes"(7)));
8783     }
8784 
8785     // Test roll!"seconds"().
8786     @safe unittest
8787     {
8788         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
8789         {
8790             orig.roll!"seconds"(seconds);
8791             assert(orig == expected);
8792         }
8793 
8794         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8795         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
8796         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
8797         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
8798         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
8799         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
8800         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
8801         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
8802         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
8803         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
8804         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
8805         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
8806         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8807         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
8808 
8809         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
8810         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
8811         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
8812         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
8813         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
8814         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
8815         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
8816         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
8817 
8818         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
8819         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
8820         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
8821         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
8822         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
8823         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
8824         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
8825         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
8826         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
8827         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
8828         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
8829         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8830         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
8831 
8832         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
8833         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
8834         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
8835 
8836         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
8837         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
8838         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
8839 
8840         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
8841         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
8842         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
8843 
8844         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
8845         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
8846         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
8847 
8848         auto tod = TimeOfDay(12, 27, 2);
8849         tod.roll!"seconds"(105).roll!"seconds"(-77);
8850         assert(tod == TimeOfDay(12, 27, 30));
8851 
8852         const ctod = TimeOfDay(0, 0, 0);
8853         immutable itod = TimeOfDay(0, 0, 0);
8854         static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
8855         static assert(!__traits(compiles, itod.roll!"seconds"(7)));
8856     }
8857 
8858 
8859     import core.time : Duration;
8860     /++
8861         Gives the result of adding or subtracting a $(REF Duration, core,time)
8862         from this $(LREF TimeOfDay).
8863 
8864         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8865         are
8866 
8867         $(BOOKTABLE,
8868         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8869         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8870         $(TR $(TD Duration) $(TD +) $(TD TimeOfDay) $(TD -->) $(TD TimeOfDay))
8871         )
8872 
8873         Params:
8874             duration = The $(REF Duration, core,time) to add to or subtract from
8875                        this $(LREF TimeOfDay).
8876       +/
8877     TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
8878     if (op == "+" || op == "-")
8879     {
8880         TimeOfDay retval = this;
8881         immutable seconds = duration.total!"seconds";
8882         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
8883     }
8884 
8885     /// ditto
8886     TimeOfDay opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
8887     if (op == "+")
8888     {
8889         return this + duration;
8890     }
8891 
8892     ///
8893     @safe unittest
8894     {
8895         import core.time : hours, minutes, seconds;
8896 
8897         assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
8898         assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
8899         assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
8900         assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
8901 
8902         assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
8903         assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
8904         assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
8905         assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
8906 
8907         assert(TimeOfDay(12, 12, 12) + seconds(1) == seconds(1) + TimeOfDay(12, 12, 12));
8908     }
8909 
8910     @safe unittest
8911     {
8912         auto tod = TimeOfDay(12, 30, 33);
8913 
8914         import core.time : dur;
8915         assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8916         assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8917         assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8918         assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8919         assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8920         assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8921 
8922         assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8923         assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8924         assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8925         assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8926         assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8927         assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8928 
8929         assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8930         assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8931         assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8932         assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8933         assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8934         assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8935 
8936         assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8937         assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8938         assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8939         assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8940         assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8941         assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8942 
8943         auto duration = dur!"hours"(11);
8944         const ctod = TimeOfDay(12, 30, 33);
8945         immutable itod = TimeOfDay(12, 30, 33);
8946         assert(tod + duration == TimeOfDay(23, 30, 33));
8947         assert(ctod + duration == TimeOfDay(23, 30, 33));
8948         assert(itod + duration == TimeOfDay(23, 30, 33));
8949 
8950         assert(tod - duration == TimeOfDay(1, 30, 33));
8951         assert(ctod - duration == TimeOfDay(1, 30, 33));
8952         assert(itod - duration == TimeOfDay(1, 30, 33));
8953     }
8954 
8955 
8956     /++
8957         Gives the result of adding or subtracting a $(REF Duration, core,time)
8958         from this $(LREF TimeOfDay), as well as assigning the result to this
8959         $(LREF TimeOfDay).
8960 
8961         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8962         are
8963 
8964         $(BOOKTABLE,
8965         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8966         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8967         )
8968 
8969         Params:
8970             duration = The $(REF Duration, core,time) to add to or subtract from
8971                        this $(LREF TimeOfDay).
8972       +/
8973     ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
8974     if (op == "+" || op == "-")
8975     {
8976         immutable seconds = duration.total!"seconds";
8977         mixin("return _addSeconds(" ~ op ~ "seconds);");
8978     }
8979 
8980     @safe unittest
8981     {
8982         import core.time : dur;
8983         auto duration = dur!"hours"(12);
8984 
8985         assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8986         assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8987         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8988         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8989         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8990         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8991 
8992         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8993         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8994         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8995         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8996         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8997         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8998 
8999         assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
9000         assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
9001         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
9002         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
9003         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
9004         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
9005 
9006         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
9007         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
9008         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
9009         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
9010         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
9011         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
9012 
9013         auto tod = TimeOfDay(19, 17, 22);
9014         (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
9015         assert(tod == TimeOfDay(17, 15, 59));
9016 
9017         const ctod = TimeOfDay(12, 33, 30);
9018         immutable itod = TimeOfDay(12, 33, 30);
9019         static assert(!__traits(compiles, ctod += duration));
9020         static assert(!__traits(compiles, itod += duration));
9021         static assert(!__traits(compiles, ctod -= duration));
9022         static assert(!__traits(compiles, itod -= duration));
9023     }
9024 
9025 
9026     /++
9027         Gives the difference between two $(LREF TimeOfDay)s.
9028 
9029         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
9030         are
9031 
9032         $(BOOKTABLE,
9033         $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
9034         )
9035 
9036         Params:
9037             rhs = The $(LREF TimeOfDay) to subtract from this one.
9038       +/
9039     Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
9040     if (op == "-")
9041     {
9042         immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
9043         immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
9044 
9045         import core.time : dur;
9046         return dur!"seconds"(lhsSec - rhsSec);
9047     }
9048 
9049     @safe unittest
9050     {
9051         auto tod = TimeOfDay(12, 30, 33);
9052 
9053         import core.time : dur;
9054         assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
9055         assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
9056         assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
9057         assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
9058         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
9059         assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
9060         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
9061         assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
9062 
9063         const ctod = TimeOfDay(12, 30, 33);
9064         immutable itod = TimeOfDay(12, 30, 33);
9065         assert(tod - tod == Duration.zero);
9066         assert(ctod - tod == Duration.zero);
9067         assert(itod - tod == Duration.zero);
9068 
9069         assert(tod - ctod == Duration.zero);
9070         assert(ctod - ctod == Duration.zero);
9071         assert(itod - ctod == Duration.zero);
9072 
9073         assert(tod - itod == Duration.zero);
9074         assert(ctod - itod == Duration.zero);
9075         assert(itod - itod == Duration.zero);
9076     }
9077 
9078 
9079     /++
9080         Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
9081         If `writer` is set, the resulting string will be written directly to it.
9082 
9083         Params:
9084             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9085         Returns:
9086             A `string` when not using an output range; `void` otherwise.
9087       +/
9088     string toISOString() const @safe pure nothrow
9089     {
9090         import std.array : appender;
9091         auto w = appender!string();
9092         w.reserve(6);
9093         try
9094             toISOString(w);
9095         catch (Exception e)
9096             assert(0, "toISOString() threw.");
9097         return w.data;
9098     }
9099 
9100     /// ditto
9101     void toISOString(W)(ref W writer) const
9102     if (isOutputRange!(W, char))
9103     {
9104         import std.format.write : formattedWrite;
9105         formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
9106     }
9107 
9108     ///
9109     @safe unittest
9110     {
9111         assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
9112         assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
9113     }
9114 
9115     @safe unittest
9116     {
9117         auto tod = TimeOfDay(12, 30, 33);
9118         const ctod = TimeOfDay(12, 30, 33);
9119         immutable itod = TimeOfDay(12, 30, 33);
9120         assert(tod.toISOString() == "123033");
9121         assert(ctod.toISOString() == "123033");
9122         assert(itod.toISOString() == "123033");
9123     }
9124 
9125 
9126     /++
9127         Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
9128         If `writer` is set, the resulting string will be written directly to it.
9129 
9130         Params:
9131             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9132         Returns:
9133             A `string` when not using an output range; `void` otherwise.
9134       +/
9135     string toISOExtString() const @safe pure nothrow
9136     {
9137         import std.array : appender;
9138         auto w = appender!string();
9139         w.reserve(8);
9140         try
9141             toISOExtString(w);
9142         catch (Exception e)
9143             assert(0, "toISOExtString() threw.");
9144         return w.data;
9145     }
9146 
9147     /// ditto
9148     void toISOExtString(W)(ref W writer) const
9149     if (isOutputRange!(W, char))
9150     {
9151         import std.format.write : formattedWrite;
9152         formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
9153     }
9154 
9155     ///
9156     @safe unittest
9157     {
9158         assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
9159         assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
9160     }
9161 
9162     @safe unittest
9163     {
9164         auto tod = TimeOfDay(12, 30, 33);
9165         const ctod = TimeOfDay(12, 30, 33);
9166         immutable itod = TimeOfDay(12, 30, 33);
9167         assert(tod.toISOExtString() == "12:30:33");
9168         assert(ctod.toISOExtString() == "12:30:33");
9169         assert(itod.toISOExtString() == "12:30:33");
9170     }
9171 
9172 
9173     /++
9174         Converts this TimeOfDay to a string.
9175 
9176         This function exists to make it easy to convert a $(LREF TimeOfDay) to a
9177         string for code that does not care what the exact format is - just that
9178         it presents the information in a clear manner. It also makes it easy to
9179         simply convert a $(LREF TimeOfDay) to a string when using functions such
9180         as `to!string`, `format`, or `writeln` which use toString to convert
9181         user-defined types. So, it is unlikely that much code will call
9182         toString directly.
9183 
9184         The format of the string is purposefully unspecified, and code that
9185         cares about the format of the string should use `toISOString`,
9186         `toISOExtString`, or some other custom formatting function that
9187         explicitly generates the format that the code needs. The reason is that
9188         the code is then clear about what format it's using, making it less
9189         error-prone to maintain the code and interact with other software that
9190         consumes the generated strings. It's for this same reason that
9191         $(LREF TimeOfDay) has no `fromString` function, whereas it does have
9192         `fromISOString` and `fromISOExtString`.
9193 
9194         The format returned by toString may or may not change in the future.
9195 
9196         Params:
9197             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9198         Returns:
9199             A `string` when not using an output range; `void` otherwise.
9200       +/
9201     string toString() const @safe pure nothrow
9202     {
9203         return toISOExtString();
9204     }
9205 
9206     /// ditto
9207     void toString(W)(ref W writer) const
9208     if (isOutputRange!(W, char))
9209     {
9210         toISOExtString(writer);
9211     }
9212 
9213     @safe unittest
9214     {
9215         auto tod = TimeOfDay(12, 30, 33);
9216         const ctod = TimeOfDay(12, 30, 33);
9217         immutable itod = TimeOfDay(12, 30, 33);
9218         assert(tod.toString());
9219         assert(ctod.toString());
9220         assert(itod.toString());
9221     }
9222 
9223 
9224     /++
9225         Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
9226         Whitespace is stripped from the given string.
9227 
9228         Params:
9229             isoString = A string formatted in the ISO format for times.
9230 
9231         Throws:
9232             $(REF DateTimeException,std,datetime,date) if the given string is
9233             not in the ISO format or if the resulting $(LREF TimeOfDay) would
9234             not be valid.
9235       +/
9236     static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
9237     if (isSomeString!S)
9238     {
9239         import std.conv : to, text, ConvException;
9240         import std.exception : enforce;
9241         import std.string : strip;
9242 
9243         int hours, minutes, seconds;
9244         auto str = strip(isoString);
9245 
9246         enforce!DateTimeException(str.length == 6, text("Invalid format for TimeOfDay.fromISOString: ", isoString));
9247 
9248         try
9249         {
9250             // cast to int from uint is used because it checks for
9251             // non digits without extra loops
9252             hours = cast(int) to!uint(str[0 .. 2]);
9253             minutes = cast(int) to!uint(str[2 .. 4]);
9254             seconds = cast(int) to!uint(str[4 .. $]);
9255         }
9256         catch (ConvException)
9257         {
9258             throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOString: ", isoString));
9259         }
9260 
9261         return TimeOfDay(hours, minutes, seconds);
9262     }
9263 
9264     ///
9265     @safe unittest
9266     {
9267         assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
9268         assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
9269         assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
9270     }
9271 
9272     @safe unittest
9273     {
9274         assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
9275         assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
9276         assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
9277         assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
9278         assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
9279         assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
9280         assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
9281         assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
9282         assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
9283         assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
9284         assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
9285         assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
9286         assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
9287         assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
9288         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
9289         assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
9290         assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
9291         assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
9292         assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
9293         assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
9294         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
9295         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
9296 
9297         assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
9298         assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
9299         assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
9300         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
9301         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
9302         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
9303         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
9304         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
9305         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
9306         assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
9307         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
9308         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
9309         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
9310         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
9311         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
9312         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
9313         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
9314         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
9315         assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
9316         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
9317         assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
9318         assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
9319         assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
9320         assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
9321         assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
9322         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
9323         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
9324 
9325         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
9326 
9327         assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
9328         assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
9329         assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
9330         assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
9331         assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
9332         assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
9333     }
9334 
9335     // https://issues.dlang.org/show_bug.cgi?id=17801
9336     @safe unittest
9337     {
9338         import std.conv : to;
9339         import std.meta : AliasSeq;
9340         static foreach (C; AliasSeq!(char, wchar, dchar))
9341         {
9342             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9343                 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
9344         }
9345     }
9346 
9347 
9348     /++
9349         Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
9350         Whitespace is stripped from the given string.
9351 
9352         Params:
9353             isoExtString = A string formatted in the ISO Extended format for
9354             times.
9355 
9356         Throws:
9357             $(REF DateTimeException,std,datetime,date) if the given string is
9358             not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
9359             would not be valid.
9360       +/
9361     static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
9362     if (isSomeString!S)
9363     {
9364         import std.conv : ConvException, text, to;
9365         import std.string : strip;
9366 
9367         auto str = strip(isoExtString);
9368         int hours, minutes, seconds;
9369 
9370         if (str.length != 8 || str[2] != ':' || str[5] != ':')
9371             throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOExtString: ", isoExtString));
9372 
9373         try
9374         {
9375             // cast to int from uint is used because it checks for
9376             // non digits without extra loops
9377             hours = cast(int) to!uint(str[0 .. 2]);
9378             minutes = cast(int) to!uint(str[3 .. 5]);
9379             seconds = cast(int) to!uint(str[6 .. $]);
9380         }
9381         catch (ConvException)
9382         {
9383             throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOExtString: ", isoExtString));
9384         }
9385 
9386         return TimeOfDay(hours, minutes, seconds);
9387     }
9388 
9389     ///
9390     @safe unittest
9391     {
9392         assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
9393         assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
9394         assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
9395     }
9396 
9397     @safe unittest
9398     {
9399         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
9400         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
9401         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
9402         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
9403         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
9404         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
9405         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
9406         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
9407         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
9408         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
9409         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
9410         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
9411         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
9412         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
9413         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
9414         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
9415         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
9416         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
9417         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
9418         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
9419         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
9420         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
9421 
9422         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
9423         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
9424         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
9425         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
9426         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
9427         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
9428         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
9429         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
9430         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
9431         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
9432         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
9433         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
9434         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
9435         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
9436         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
9437         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
9438         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
9439         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
9440         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
9441         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
9442         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
9443         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
9444         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
9445         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
9446         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
9447         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
9448         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
9449 
9450         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
9451 
9452         assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
9453         assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
9454         assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
9455         assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
9456         assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
9457         assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
9458     }
9459 
9460     // https://issues.dlang.org/show_bug.cgi?id=17801
9461     @safe unittest
9462     {
9463         import std.conv : to;
9464         import std.meta : AliasSeq;
9465         static foreach (C; AliasSeq!(char, wchar, dchar))
9466         {
9467             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9468                 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
9469         }
9470     }
9471 
9472 
9473     /++
9474         Returns midnight.
9475       +/
9476     @property static TimeOfDay min() @safe pure nothrow @nogc
9477     {
9478         return TimeOfDay.init;
9479     }
9480 
9481     @safe unittest
9482     {
9483         assert(TimeOfDay.min.hour == 0);
9484         assert(TimeOfDay.min.minute == 0);
9485         assert(TimeOfDay.min.second == 0);
9486         assert(TimeOfDay.min < TimeOfDay.max);
9487     }
9488 
9489 
9490     /++
9491         Returns one second short of midnight.
9492       +/
9493     @property static TimeOfDay max() @safe pure nothrow @nogc
9494     {
9495         auto tod = TimeOfDay.init;
9496         tod._hour = maxHour;
9497         tod._minute = maxMinute;
9498         tod._second = maxSecond;
9499 
9500         return tod;
9501     }
9502 
9503     @safe unittest
9504     {
9505         assert(TimeOfDay.max.hour == 23);
9506         assert(TimeOfDay.max.minute == 59);
9507         assert(TimeOfDay.max.second == 59);
9508         assert(TimeOfDay.max > TimeOfDay.min);
9509     }
9510 
9511 
9512 private:
9513 
9514     /+
9515         Add seconds to the time of day. Negative values will subtract. If the
9516         number of seconds overflows (or underflows), then the seconds will wrap,
9517         increasing (or decreasing) the number of minutes accordingly. If the
9518         number of minutes overflows (or underflows), then the minutes will wrap.
9519         If the number of minutes overflows(or underflows), then the hour will
9520         wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
9521 
9522         Params:
9523             seconds = The number of seconds to add to this TimeOfDay.
9524       +/
9525     ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
9526     {
9527         import core.time : convert;
9528         long hnsecs = convert!("seconds", "hnsecs")(seconds);
9529         hnsecs += convert!("hours", "hnsecs")(_hour);
9530         hnsecs += convert!("minutes", "hnsecs")(_minute);
9531         hnsecs += convert!("seconds", "hnsecs")(_second);
9532 
9533         hnsecs %= convert!("days", "hnsecs")(1);
9534 
9535         if (hnsecs < 0)
9536             hnsecs += convert!("days", "hnsecs")(1);
9537 
9538         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
9539         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9540         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
9541 
9542         _hour = cast(ubyte) newHours;
9543         _minute = cast(ubyte) newMinutes;
9544         _second = cast(ubyte) newSeconds;
9545 
9546         return this;
9547     }
9548 
9549     @safe unittest
9550     {
9551         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
9552         {
9553             orig._addSeconds(seconds);
9554             assert(orig == expected);
9555         }
9556 
9557         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
9558         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
9559         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
9560         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
9561         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
9562         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
9563         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
9564         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
9565         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
9566         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
9567         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
9568         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
9569         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
9570         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
9571 
9572         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
9573         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
9574         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
9575         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
9576         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
9577         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
9578         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
9579         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
9580 
9581         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
9582         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
9583         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
9584         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
9585         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
9586         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
9587         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
9588         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
9589         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
9590         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
9591         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
9592         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
9593         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
9594 
9595         testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
9596         testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
9597         testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
9598         testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
9599         testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
9600         testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
9601 
9602         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
9603         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
9604         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
9605 
9606         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
9607         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
9608         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
9609 
9610         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
9611         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
9612         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
9613 
9614         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
9615         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
9616         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
9617 
9618         const ctod = TimeOfDay(0, 0, 0);
9619         immutable itod = TimeOfDay(0, 0, 0);
9620         static assert(!__traits(compiles, ctod._addSeconds(7)));
9621         static assert(!__traits(compiles, itod._addSeconds(7)));
9622     }
9623 
9624 
9625     /+
9626         Whether the given values form a valid $(LREF TimeOfDay).
9627      +/
9628     static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
9629     {
9630         return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
9631     }
9632 
9633 
9634     @safe pure invariant()
9635     {
9636         import std.format : format;
9637         assert(_valid(_hour, _minute, _second),
9638                format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
9639     }
9640 
9641 
9642 package:
9643 
9644     ubyte _hour;
9645     ubyte _minute;
9646     ubyte _second;
9647 
9648     enum ubyte maxHour   = 24 - 1;
9649     enum ubyte maxMinute = 60 - 1;
9650     enum ubyte maxSecond = 60 - 1;
9651 }
9652 
9653 ///
9654 @safe pure unittest
9655 {
9656     import core.time : minutes, seconds;
9657 
9658     auto t = TimeOfDay(12, 30, 0);
9659 
9660     t += 10.minutes + 100.seconds;
9661     assert(t == TimeOfDay(12, 41, 40));
9662 
9663     assert(t.toISOExtString() == "12:41:40");
9664     assert(t.toISOString() == "124140");
9665 
9666     assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
9667     assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
9668 }
9669 
9670 /++
9671     Returns whether the given value is valid for the given unit type when in a
9672     time point. Naturally, a duration is not held to a particular range, but
9673     the values in a time point are (e.g. a month must be in the range of
9674     1 - 12 inclusive).
9675 
9676     Params:
9677         units = The units of time to validate.
9678         value = The number to validate.
9679   +/
9680 bool valid(string units)(int value) @safe pure nothrow @nogc
9681 if (units == "months" ||
9682     units == "hours" ||
9683     units == "minutes" ||
9684     units == "seconds")
9685 {
9686     static if (units == "months")
9687         return value >= Month.jan && value <= Month.dec;
9688     else static if (units == "hours")
9689         return value >= 0 && value <= 23;
9690     else static if (units == "minutes")
9691         return value >= 0 && value <= 59;
9692     else static if (units == "seconds")
9693         return value >= 0 && value <= 59;
9694 }
9695 
9696 ///
9697 @safe unittest
9698 {
9699     assert(valid!"hours"(12));
9700     assert(!valid!"hours"(32));
9701     assert(valid!"months"(12));
9702     assert(!valid!"months"(13));
9703 }
9704 
9705 /++
9706     Returns whether the given day is valid for the given year and month.
9707 
9708     Params:
9709         units = The units of time to validate.
9710         year  = The year of the day to validate.
9711         month = The month of the day to validate (January is 1).
9712         day   = The day to validate.
9713   +/
9714 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9715 if (units == "days")
9716 {
9717     return day > 0 && day <= maxDay(year, month);
9718 }
9719 
9720 ///
9721 @safe pure nothrow @nogc unittest
9722 {
9723     assert(valid!"days"(2016, 2, 29));
9724     assert(!valid!"days"(2016, 2, 30));
9725     assert(valid!"days"(2017, 2, 20));
9726     assert(!valid!"days"(2017, 2, 29));
9727 }
9728 
9729 private short castToYear(int year, string file = __FILE__, size_t line = __LINE__) @safe pure
9730 {
9731     import std.conv : to, ConvOverflowException;
9732     import std.format : format;
9733 
9734     try
9735         return year.to!short;
9736     catch (ConvOverflowException)
9737         throw new DateTimeException(format("year %s doesn't fit to Date.", year), file, line);
9738 }
9739 
9740 /++
9741     Params:
9742         units = The units of time to validate.
9743         value = The number to validate.
9744         file  = The file that the $(LREF DateTimeException) will list if thrown.
9745         line  = The line number that the $(LREF DateTimeException) will list if
9746                 thrown.
9747 
9748     Throws:
9749         $(LREF DateTimeException) if `valid!units(value)` is false.
9750   +/
9751 void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9752 if (units == "months" ||
9753     units == "hours" ||
9754     units == "minutes" ||
9755     units == "seconds")
9756 {
9757     import std.format : format;
9758 
9759     static if (units == "months")
9760     {
9761         if (!valid!units(value))
9762             throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
9763     }
9764     else static if (units == "hours")
9765     {
9766         if (!valid!units(value))
9767             throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
9768     }
9769     else static if (units == "minutes")
9770     {
9771         if (!valid!units(value))
9772             throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
9773     }
9774     else static if (units == "seconds")
9775     {
9776         if (!valid!units(value))
9777             throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
9778     }
9779 }
9780 
9781 ///
9782 @safe pure unittest
9783 {
9784     import std.exception : assertThrown, assertNotThrown;
9785 
9786     assertNotThrown(enforceValid!"months"(10));
9787     assertNotThrown(enforceValid!"seconds"(40));
9788 
9789     assertThrown!DateTimeException(enforceValid!"months"(0));
9790     assertThrown!DateTimeException(enforceValid!"hours"(24));
9791     assertThrown!DateTimeException(enforceValid!"minutes"(60));
9792     assertThrown!DateTimeException(enforceValid!"seconds"(60));
9793 }
9794 
9795 
9796 /++
9797     Because the validity of the day number depends on both on the year
9798     and month of which the day is occurring, take all three variables
9799     to validate the day.
9800 
9801     Params:
9802         units = The units of time to validate.
9803         year  = The year of the day to validate.
9804         month = The month of the day to validate.
9805         day   = The day to validate.
9806         file  = The file that the $(LREF DateTimeException) will list if thrown.
9807         line  = The line number that the $(LREF DateTimeException) will list if
9808                 thrown.
9809 
9810     Throws:
9811         $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
9812   +/
9813 void enforceValid(string units)
9814                  (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9815 if (units == "days")
9816 {
9817     import std.format : format;
9818     if (!valid!"days"(year, month, day))
9819         throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
9820 }
9821 
9822 ///
9823 @safe pure unittest
9824 {
9825     import std.exception : assertThrown, assertNotThrown;
9826 
9827     assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
9828     // leap year
9829     assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
9830 
9831     assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
9832     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
9833     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
9834 }
9835 
9836 
9837 /++
9838     Returns the number of days from the current day of the week to the given
9839     day of the week. If they are the same, then the result is 0.
9840 
9841     Params:
9842         currDoW = The current day of the week.
9843         dow     = The day of the week to get the number of days to.
9844   +/
9845 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
9846 {
9847     if (currDoW == dow)
9848         return 0;
9849     if (currDoW < dow)
9850         return dow - currDoW;
9851     return DayOfWeek.sat - currDoW + dow + 1;
9852 }
9853 
9854 ///
9855 @safe pure nothrow @nogc unittest
9856 {
9857     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9858     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9859     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9860 }
9861 
9862 @safe unittest
9863 {
9864     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
9865     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
9866     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
9867     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
9868     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
9869     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
9870     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
9871 
9872     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9873     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9874     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
9875     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9876     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
9877     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
9878     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
9879 
9880     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
9881     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
9882     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
9883     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
9884     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
9885     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
9886     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
9887 
9888     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
9889     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
9890     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
9891     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
9892     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
9893     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
9894     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
9895 
9896     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
9897     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
9898     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
9899     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
9900     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
9901     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
9902     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
9903 
9904     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
9905     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
9906     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
9907     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
9908     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
9909     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
9910     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
9911 
9912     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
9913     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
9914     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
9915     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
9916     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
9917     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
9918     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
9919 }
9920 
9921 
9922 /++
9923     Returns the number of months from the current months of the year to the
9924     given month of the year. If they are the same, then the result is 0.
9925 
9926     Params:
9927         currMonth = The current month of the year.
9928         month     = The month of the year to get the number of months to.
9929   +/
9930 int monthsToMonth(int currMonth, int month) @safe pure
9931 {
9932     enforceValid!"months"(currMonth);
9933     enforceValid!"months"(month);
9934 
9935     if (currMonth == month)
9936         return 0;
9937     if (currMonth < month)
9938         return month - currMonth;
9939     return Month.dec - currMonth + month;
9940 }
9941 
9942 ///
9943 @safe pure unittest
9944 {
9945     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9946     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9947     assert(monthsToMonth(Month.jul, Month.oct) == 3);
9948 }
9949 
9950 @safe unittest
9951 {
9952     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9953     assert(monthsToMonth(Month.jan, Month.feb) == 1);
9954     assert(monthsToMonth(Month.jan, Month.mar) == 2);
9955     assert(monthsToMonth(Month.jan, Month.apr) == 3);
9956     assert(monthsToMonth(Month.jan, Month.may) == 4);
9957     assert(monthsToMonth(Month.jan, Month.jun) == 5);
9958     assert(monthsToMonth(Month.jan, Month.jul) == 6);
9959     assert(monthsToMonth(Month.jan, Month.aug) == 7);
9960     assert(monthsToMonth(Month.jan, Month.sep) == 8);
9961     assert(monthsToMonth(Month.jan, Month.oct) == 9);
9962     assert(monthsToMonth(Month.jan, Month.nov) == 10);
9963     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9964 
9965     assert(monthsToMonth(Month.may, Month.jan) == 8);
9966     assert(monthsToMonth(Month.may, Month.feb) == 9);
9967     assert(monthsToMonth(Month.may, Month.mar) == 10);
9968     assert(monthsToMonth(Month.may, Month.apr) == 11);
9969     assert(monthsToMonth(Month.may, Month.may) == 0);
9970     assert(monthsToMonth(Month.may, Month.jun) == 1);
9971     assert(monthsToMonth(Month.may, Month.jul) == 2);
9972     assert(monthsToMonth(Month.may, Month.aug) == 3);
9973     assert(monthsToMonth(Month.may, Month.sep) == 4);
9974     assert(monthsToMonth(Month.may, Month.oct) == 5);
9975     assert(monthsToMonth(Month.may, Month.nov) == 6);
9976     assert(monthsToMonth(Month.may, Month.dec) == 7);
9977 
9978     assert(monthsToMonth(Month.oct, Month.jan) == 3);
9979     assert(monthsToMonth(Month.oct, Month.feb) == 4);
9980     assert(monthsToMonth(Month.oct, Month.mar) == 5);
9981     assert(monthsToMonth(Month.oct, Month.apr) == 6);
9982     assert(monthsToMonth(Month.oct, Month.may) == 7);
9983     assert(monthsToMonth(Month.oct, Month.jun) == 8);
9984     assert(monthsToMonth(Month.oct, Month.jul) == 9);
9985     assert(monthsToMonth(Month.oct, Month.aug) == 10);
9986     assert(monthsToMonth(Month.oct, Month.sep) == 11);
9987     assert(monthsToMonth(Month.oct, Month.oct) == 0);
9988     assert(monthsToMonth(Month.oct, Month.nov) == 1);
9989     assert(monthsToMonth(Month.oct, Month.dec) == 2);
9990 
9991     assert(monthsToMonth(Month.dec, Month.jan) == 1);
9992     assert(monthsToMonth(Month.dec, Month.feb) == 2);
9993     assert(monthsToMonth(Month.dec, Month.mar) == 3);
9994     assert(monthsToMonth(Month.dec, Month.apr) == 4);
9995     assert(monthsToMonth(Month.dec, Month.may) == 5);
9996     assert(monthsToMonth(Month.dec, Month.jun) == 6);
9997     assert(monthsToMonth(Month.dec, Month.jul) == 7);
9998     assert(monthsToMonth(Month.dec, Month.aug) == 8);
9999     assert(monthsToMonth(Month.dec, Month.sep) == 9);
10000     assert(monthsToMonth(Month.dec, Month.oct) == 10);
10001     assert(monthsToMonth(Month.dec, Month.nov) == 11);
10002     assert(monthsToMonth(Month.dec, Month.dec) == 0);
10003 }
10004 
10005 
10006 /++
10007     Whether the given Gregorian Year is a leap year.
10008 
10009     Params:
10010         year = The year to to be tested.
10011  +/
10012 bool yearIsLeapYear(int year) @safe pure nothrow @nogc
10013 {
10014     if (year % 400 == 0)
10015         return true;
10016     if (year % 100 == 0)
10017         return false;
10018     return year % 4 == 0;
10019 }
10020 
10021 ///
10022 @safe unittest
10023 {
10024     foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
10025     {
10026         assert(!yearIsLeapYear(year));
10027         assert(!yearIsLeapYear(-year));
10028     }
10029 
10030     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
10031     {
10032         assert(yearIsLeapYear(year));
10033         assert(yearIsLeapYear(-year));
10034     }
10035 }
10036 
10037 @safe unittest
10038 {
10039     import std.format : format;
10040     foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
10041                     2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
10042     {
10043         assert(!yearIsLeapYear(year), format("year: %s.", year));
10044         assert(!yearIsLeapYear(-year), format("year: %s.", year));
10045     }
10046 
10047     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
10048     {
10049         assert(yearIsLeapYear(year), format("year: %s.", year));
10050         assert(yearIsLeapYear(-year), format("year: %s.", year));
10051     }
10052 }
10053 
10054 
10055 /++
10056     Whether the given type defines all of the necessary functions for it to
10057     function as a time point.
10058 
10059     1. `T` must define a static property named `min` which is the smallest
10060        value of `T` as `Unqual!T`.
10061 
10062     2. `T` must define a static property named `max` which is the largest
10063        value of `T` as `Unqual!T`.
10064 
10065     3. `T` must define an `opBinary` for addition and subtraction that
10066        accepts $(REF Duration, core,time) and returns `Unqual!T`.
10067 
10068     4. `T` must define an `opOpAssign` for addition and subtraction that
10069        accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
10070 
10071     5. `T` must define a `opBinary` for subtraction which accepts `T`
10072        and returns $(REF Duration, core,time).
10073   +/
10074 template isTimePoint(T)
10075 {
10076     import core.time : Duration;
10077     import std.traits : FunctionAttribute, functionAttributes, Unqual;
10078 
10079     enum isTimePoint = hasMin &&
10080                        hasMax &&
10081                        hasOverloadedOpBinaryWithDuration &&
10082                        hasOverloadedOpAssignWithDuration &&
10083                        hasOverloadedOpBinaryWithSelf &&
10084                        !is(U == Duration);
10085 
10086 private:
10087 
10088     alias U = Unqual!T;
10089 
10090     enum hasMin = __traits(hasMember, T, "min") &&
10091                   is(typeof(T.min) == U) &&
10092                   is(typeof({static assert(__traits(isStaticFunction, T.min));}));
10093 
10094     enum hasMax = __traits(hasMember, T, "max") &&
10095                   is(typeof(T.max) == U) &&
10096                   is(typeof({static assert(__traits(isStaticFunction, T.max));}));
10097 
10098     enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
10099                                              is(typeof(T.init - Duration.init) == U);
10100 
10101     enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
10102                                              is(typeof(U.init -= Duration.init) == U) &&
10103                                              is(typeof(
10104                                              {
10105                                                  alias add = U.opOpAssign!"+";
10106                                                  alias sub = U.opOpAssign!"-";
10107                                                  alias FA = FunctionAttribute;
10108                                                  static assert((functionAttributes!add & FA.ref_) != 0);
10109                                                  static assert((functionAttributes!sub & FA.ref_) != 0);
10110                                              }));
10111 
10112     enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
10113 }
10114 
10115 ///
10116 @safe unittest
10117 {
10118     import core.time : Duration;
10119     import std.datetime.interval : Interval;
10120     import std.datetime.systime : SysTime;
10121 
10122     static assert(isTimePoint!Date);
10123     static assert(isTimePoint!DateTime);
10124     static assert(isTimePoint!SysTime);
10125     static assert(isTimePoint!TimeOfDay);
10126 
10127     static assert(!isTimePoint!int);
10128     static assert(!isTimePoint!Duration);
10129     static assert(!isTimePoint!(Interval!SysTime));
10130 }
10131 
10132 @safe unittest
10133 {
10134     import core.time;
10135     import std.datetime.interval;
10136     import std.datetime.systime;
10137     import std.meta : AliasSeq;
10138 
10139     static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
10140     {
10141         static assert(isTimePoint!(const TP), TP.stringof);
10142         static assert(isTimePoint!(immutable TP), TP.stringof);
10143     }
10144 
10145     static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
10146         static assert(!isTimePoint!T, T.stringof);
10147 }
10148 
10149 
10150 /++
10151     Whether all of the given strings are valid units of time.
10152 
10153     `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
10154     can handle precision greater than hnsecs, and the few functions in core.time
10155     which deal with "nsecs" deal with it explicitly.
10156   +/
10157 bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
10158 {
10159     import std.algorithm.searching : canFind;
10160     foreach (str; units)
10161     {
10162         if (!canFind(timeStrings[], str))
10163             return false;
10164     }
10165     return true;
10166 }
10167 
10168 ///
10169 @safe @nogc nothrow unittest
10170 {
10171     assert(validTimeUnits("msecs", "seconds", "minutes"));
10172     assert(validTimeUnits("days", "weeks", "months"));
10173     assert(!validTimeUnits("ms", "seconds", "minutes"));
10174 }
10175 
10176 
10177 /++
10178     Compares two time unit strings. `"years"` are the largest units and
10179     `"hnsecs"` are the smallest.
10180 
10181     Returns:
10182         $(BOOKTABLE,
10183         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10184         $(TR $(TD this == rhs) $(TD 0))
10185         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10186         )
10187 
10188     Throws:
10189         $(LREF DateTimeException) if either of the given strings is not a valid
10190         time unit string.
10191  +/
10192 int cmpTimeUnits(string lhs, string rhs) @safe pure
10193 {
10194     import std.algorithm.searching : countUntil;
10195     import std.exception : enforce;
10196     import std.format : format;
10197 
10198     immutable indexOfLHS = countUntil(timeStrings, lhs);
10199     immutable indexOfRHS = countUntil(timeStrings, rhs);
10200 
10201     enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
10202     enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
10203 
10204     if (indexOfLHS < indexOfRHS)
10205         return -1;
10206     if (indexOfLHS > indexOfRHS)
10207         return 1;
10208 
10209     return 0;
10210 }
10211 
10212 ///
10213 @safe pure unittest
10214 {
10215     import std.exception : assertThrown;
10216 
10217     assert(cmpTimeUnits("hours", "hours") == 0);
10218     assert(cmpTimeUnits("hours", "weeks") < 0);
10219     assert(cmpTimeUnits("months", "seconds") > 0);
10220 
10221     assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
10222 }
10223 
10224 @safe unittest
10225 {
10226     foreach (i, outerUnits; timeStrings)
10227     {
10228         assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
10229 
10230         // For some reason, $ won't compile.
10231         foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
10232             assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
10233     }
10234 
10235     foreach (i, outerUnits; timeStrings)
10236     {
10237         foreach (innerUnits; timeStrings[0 .. i])
10238             assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
10239     }
10240 }
10241 
10242 
10243 /++
10244     Compares two time unit strings at compile time. `"years"` are the largest
10245     units and `"hnsecs"` are the smallest.
10246 
10247     This template is used instead of `cmpTimeUnits` because exceptions
10248     can't be thrown at compile time and `cmpTimeUnits` must enforce that
10249     the strings it's given are valid time unit strings. This template uses a
10250     template constraint instead.
10251 
10252     Returns:
10253         $(BOOKTABLE,
10254         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10255         $(TR $(TD this == rhs) $(TD 0))
10256         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10257         )
10258  +/
10259 template CmpTimeUnits(string lhs, string rhs)
10260 if (validTimeUnits(lhs, rhs))
10261 {
10262     enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
10263 }
10264 
10265 ///
10266 @safe pure unittest
10267 {
10268     static assert(CmpTimeUnits!("years", "weeks") > 0);
10269     static assert(CmpTimeUnits!("days", "days") == 0);
10270     static assert(CmpTimeUnits!("seconds", "hours") < 0);
10271 }
10272 
10273 // Helper function for CmpTimeUnits.
10274 private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
10275 {
10276     import std.algorithm.searching : countUntil;
10277     auto tstrings = timeStrings;
10278     immutable indexOfLHS = countUntil(tstrings, lhs);
10279     immutable indexOfRHS = countUntil(tstrings, rhs);
10280 
10281     if (indexOfLHS < indexOfRHS)
10282         return -1;
10283     if (indexOfLHS > indexOfRHS)
10284         return 1;
10285 
10286     return 0;
10287 }
10288 
10289 @safe unittest
10290 {
10291     static foreach (i; 0 .. timeStrings.length)
10292     {
10293         static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
10294 
10295         static foreach (next; timeStrings[i + 1 .. $])
10296             static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
10297 
10298         static foreach (prev; timeStrings[0 .. i])
10299             static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
10300     }
10301 }
10302 
10303 
10304 package:
10305 
10306 
10307 /+
10308     Array of the short (three letter) names of each month.
10309   +/
10310 immutable string[12] _monthNames = ["Jan",
10311                                     "Feb",
10312                                     "Mar",
10313                                     "Apr",
10314                                     "May",
10315                                     "Jun",
10316                                     "Jul",
10317                                     "Aug",
10318                                     "Sep",
10319                                     "Oct",
10320                                     "Nov",
10321                                     "Dec"];
10322 
10323 /+
10324     The maximum valid Day in the given month in the given year.
10325 
10326     Params:
10327         year  = The year to get the day for.
10328         month = The month of the Gregorian Calendar to get the day for.
10329  +/
10330 ubyte maxDay(int year, int month) @safe pure nothrow @nogc
10331 in
10332 {
10333     assert(valid!"months"(month));
10334 }
10335 do
10336 {
10337     switch (month)
10338     {
10339         case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
10340             return 31;
10341         case Month.feb:
10342             return yearIsLeapYear(year) ? 29 : 28;
10343         case Month.apr, Month.jun, Month.sep, Month.nov:
10344             return 30;
10345         default:
10346             assert(0, "Invalid month.");
10347     }
10348 }
10349 
10350 @safe unittest
10351 {
10352     // Test A.D.
10353     assert(maxDay(1999, 1) == 31);
10354     assert(maxDay(1999, 2) == 28);
10355     assert(maxDay(1999, 3) == 31);
10356     assert(maxDay(1999, 4) == 30);
10357     assert(maxDay(1999, 5) == 31);
10358     assert(maxDay(1999, 6) == 30);
10359     assert(maxDay(1999, 7) == 31);
10360     assert(maxDay(1999, 8) == 31);
10361     assert(maxDay(1999, 9) == 30);
10362     assert(maxDay(1999, 10) == 31);
10363     assert(maxDay(1999, 11) == 30);
10364     assert(maxDay(1999, 12) == 31);
10365 
10366     assert(maxDay(2000, 1) == 31);
10367     assert(maxDay(2000, 2) == 29);
10368     assert(maxDay(2000, 3) == 31);
10369     assert(maxDay(2000, 4) == 30);
10370     assert(maxDay(2000, 5) == 31);
10371     assert(maxDay(2000, 6) == 30);
10372     assert(maxDay(2000, 7) == 31);
10373     assert(maxDay(2000, 8) == 31);
10374     assert(maxDay(2000, 9) == 30);
10375     assert(maxDay(2000, 10) == 31);
10376     assert(maxDay(2000, 11) == 30);
10377     assert(maxDay(2000, 12) == 31);
10378 
10379     // Test B.C.
10380     assert(maxDay(-1999, 1) == 31);
10381     assert(maxDay(-1999, 2) == 28);
10382     assert(maxDay(-1999, 3) == 31);
10383     assert(maxDay(-1999, 4) == 30);
10384     assert(maxDay(-1999, 5) == 31);
10385     assert(maxDay(-1999, 6) == 30);
10386     assert(maxDay(-1999, 7) == 31);
10387     assert(maxDay(-1999, 8) == 31);
10388     assert(maxDay(-1999, 9) == 30);
10389     assert(maxDay(-1999, 10) == 31);
10390     assert(maxDay(-1999, 11) == 30);
10391     assert(maxDay(-1999, 12) == 31);
10392 
10393     assert(maxDay(-2000, 1) == 31);
10394     assert(maxDay(-2000, 2) == 29);
10395     assert(maxDay(-2000, 3) == 31);
10396     assert(maxDay(-2000, 4) == 30);
10397     assert(maxDay(-2000, 5) == 31);
10398     assert(maxDay(-2000, 6) == 30);
10399     assert(maxDay(-2000, 7) == 31);
10400     assert(maxDay(-2000, 8) == 31);
10401     assert(maxDay(-2000, 9) == 30);
10402     assert(maxDay(-2000, 10) == 31);
10403     assert(maxDay(-2000, 11) == 30);
10404     assert(maxDay(-2000, 12) == 31);
10405 }
10406 
10407 /+
10408     Splits out a particular unit from hnsecs and gives the value for that
10409     unit and the remaining hnsecs. It really shouldn't be used unless unless
10410     all units larger than the given units have already been split out.
10411 
10412     Params:
10413         units  = The units to split out.
10414         hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
10415                  after splitting out the given units.
10416 
10417     Returns:
10418         The number of the given units from converting hnsecs to those units.
10419   +/
10420 long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
10421 if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
10422 {
10423     import core.time : convert;
10424     immutable value = convert!("hnsecs", units)(hnsecs);
10425     hnsecs -= convert!(units, "hnsecs")(value);
10426     return value;
10427 }
10428 
10429 @safe unittest
10430 {
10431     auto hnsecs = 2595000000007L;
10432     immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
10433     assert(days == 3);
10434     assert(hnsecs == 3000000007);
10435 
10436     immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
10437     assert(minutes == 5);
10438     assert(hnsecs == 7);
10439 }
10440 
10441 
10442 /+
10443     Returns the day of the week for the given day of the Gregorian Calendar.
10444 
10445     Params:
10446         day = The day of the Gregorian Calendar for which to get the day of
10447               the week.
10448   +/
10449 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
10450 {
10451     // January 1st, 1 A.D. was a Monday
10452     if (day >= 0)
10453         return cast(DayOfWeek)(day % 7);
10454     else
10455     {
10456         immutable dow = cast(DayOfWeek)((day % 7) + 7);
10457 
10458         if (dow == 7)
10459             return DayOfWeek.sun;
10460         else
10461             return dow;
10462     }
10463 }
10464 
10465 @safe unittest
10466 {
10467     import std.datetime.systime : SysTime;
10468 
10469     // Test A.D.
10470     assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
10471     assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
10472     assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
10473     assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
10474     assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
10475     assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
10476     assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
10477     assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
10478     assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
10479     assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
10480     assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
10481     assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
10482     assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10483     assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10484     assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
10485     assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
10486     assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
10487     assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
10488     assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
10489     assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
10490     assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
10491     assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
10492 
10493     // Test B.C.
10494     assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
10495     assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
10496     assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
10497     assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
10498     assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
10499     assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
10500     assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
10501     assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
10502     assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
10503 }
10504 
10505 
10506 private:
10507 
10508 enum daysInYear     = 365;  // The number of days in a non-leap year.
10509 enum daysInLeapYear = 366;  // The numbef or days in a leap year.
10510 enum daysIn4Years   = daysInYear * 3 + daysInLeapYear;  // Number of days in 4 years.
10511 enum daysIn100Years = daysIn4Years * 25 - 1;  // The number of days in 100 years.
10512 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
10513 
10514 /+
10515     Array of integers representing the last days of each month in a year.
10516   +/
10517 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
10518 
10519 /+
10520     Array of integers representing the last days of each month in a leap year.
10521   +/
10522 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
10523 
10524 
10525 /+
10526     Returns the string representation of the given month.
10527   +/
10528 string monthToString(Month month) @safe pure
10529 {
10530     import std.format : format;
10531     assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
10532     return _monthNames[month - Month.jan];
10533 }
10534 
10535 @safe unittest
10536 {
10537     assert(monthToString(Month.jan) == "Jan");
10538     assert(monthToString(Month.feb) == "Feb");
10539     assert(monthToString(Month.mar) == "Mar");
10540     assert(monthToString(Month.apr) == "Apr");
10541     assert(monthToString(Month.may) == "May");
10542     assert(monthToString(Month.jun) == "Jun");
10543     assert(monthToString(Month.jul) == "Jul");
10544     assert(monthToString(Month.aug) == "Aug");
10545     assert(monthToString(Month.sep) == "Sep");
10546     assert(monthToString(Month.oct) == "Oct");
10547     assert(monthToString(Month.nov) == "Nov");
10548     assert(monthToString(Month.dec) == "Dec");
10549 }
10550 
10551 
10552 /+
10553     Returns the Month corresponding to the given string.
10554 
10555     Params:
10556         monthStr = The string representation of the month to get the Month for.
10557 
10558     Throws:
10559         $(REF DateTimeException,std,datetime,date) if the given month is not a
10560         valid month string.
10561   +/
10562 Month monthFromString(T)(T monthStr) @safe pure
10563 if (isSomeString!T)
10564 {
10565     import std.format : format;
10566     switch (monthStr)
10567     {
10568         case "Jan":
10569             return Month.jan;
10570         case "Feb":
10571             return Month.feb;
10572         case "Mar":
10573             return Month.mar;
10574         case "Apr":
10575             return Month.apr;
10576         case "May":
10577             return Month.may;
10578         case "Jun":
10579             return Month.jun;
10580         case "Jul":
10581             return Month.jul;
10582         case "Aug":
10583             return Month.aug;
10584         case "Sep":
10585             return Month.sep;
10586         case "Oct":
10587             return Month.oct;
10588         case "Nov":
10589             return Month.nov;
10590         case "Dec":
10591             return Month.dec;
10592         default:
10593             throw new DateTimeException(format!"Invalid month %s"(monthStr));
10594     }
10595 }
10596 
10597 @safe unittest
10598 {
10599     import std.conv : to;
10600     import std.traits : EnumMembers;
10601     foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
10602                       "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
10603     {
10604         assertThrown!DateTimeException(monthFromString(badStr), badStr);
10605     }
10606 
10607     foreach (month; EnumMembers!Month)
10608     {
10609         assert(monthFromString(monthToString(month)) == month, month.to!string);
10610     }
10611 }
10612 
10613 
10614 // NOTE: all the non-simple array literals are wrapped in functions, because
10615 // otherwise importing causes re-evaluation of the static initializers using
10616 // CTFE with unittests enabled
10617 version (StdUnittest)
10618 {
10619 private @safe:
10620     // All of these helper arrays are sorted in ascending order.
10621     auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
10622     auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
10623 
10624     // I'd use a Tuple, but I get forward reference errors if I try.
10625     struct MonthDay
10626     {
10627         Month month;
10628         short day;
10629 
10630         this(int m, short d)
10631         {
10632             month = cast(Month) m;
10633             day = d;
10634         }
10635     }
10636 
10637     MonthDay[] testMonthDays()
10638     {
10639         static MonthDay[] result = [MonthDay(1, 1),
10640                                 MonthDay(1, 2),
10641                                 MonthDay(3, 17),
10642                                 MonthDay(7, 4),
10643                                 MonthDay(10, 27),
10644                                 MonthDay(12, 30),
10645                                 MonthDay(12, 31)];
10646         return result;
10647     }
10648 
10649     auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
10650 
10651     TimeOfDay[] testTODs()
10652     {
10653         static result = [TimeOfDay(0, 0, 0),
10654                      TimeOfDay(0, 0, 1),
10655                      TimeOfDay(0, 1, 0),
10656                      TimeOfDay(1, 0, 0),
10657                      TimeOfDay(13, 13, 13),
10658                      TimeOfDay(23, 59, 59)];
10659         return result;
10660     }
10661 
10662     auto testHours = [0, 1, 12, 22, 23];
10663     auto testMinSecs = [0, 1, 30, 58, 59];
10664 
10665     // Throwing exceptions is incredibly expensive, so we want to use a smaller
10666     // set of values for tests using assertThrown.
10667     TimeOfDay[] testTODsThrown()
10668     {
10669        static result = [TimeOfDay(0, 0, 0),
10670                            TimeOfDay(13, 13, 13),
10671                            TimeOfDay(23, 59, 59)];
10672        return result;
10673     }
10674 
10675     Date[] testDatesBC;
10676     Date[] testDatesAD;
10677 
10678     DateTime[] testDateTimesBC;
10679     DateTime[] testDateTimesAD;
10680 
10681     // I'd use a Tuple, but I get forward reference errors if I try.
10682     struct GregDay { int day; Date date; }
10683     GregDay[] testGregDaysBC()
10684     {
10685        static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
10686                            GregDay(-735_233, Date(-2012, 1, 1)),
10687                            GregDay(-735_202, Date(-2012, 2, 1)),
10688                            GregDay(-735_175, Date(-2012, 2, 28)),
10689                            GregDay(-735_174, Date(-2012, 2, 29)),
10690                            GregDay(-735_173, Date(-2012, 3, 1)),
10691                            GregDay(-734_502, Date(-2010, 1, 1)),
10692                            GregDay(-734_472, Date(-2010, 1, 31)),
10693                            GregDay(-734_471, Date(-2010, 2, 1)),
10694                            GregDay(-734_444, Date(-2010, 2, 28)),
10695                            GregDay(-734_443, Date(-2010, 3, 1)),
10696                            GregDay(-734_413, Date(-2010, 3, 31)),
10697                            GregDay(-734_412, Date(-2010, 4, 1)),
10698                            GregDay(-734_383, Date(-2010, 4, 30)),
10699                            GregDay(-734_382, Date(-2010, 5, 1)),
10700                            GregDay(-734_352, Date(-2010, 5, 31)),
10701                            GregDay(-734_351, Date(-2010, 6, 1)),
10702                            GregDay(-734_322, Date(-2010, 6, 30)),
10703                            GregDay(-734_321, Date(-2010, 7, 1)),
10704                            GregDay(-734_291, Date(-2010, 7, 31)),
10705                            GregDay(-734_290, Date(-2010, 8, 1)),
10706                            GregDay(-734_260, Date(-2010, 8, 31)),
10707                            GregDay(-734_259, Date(-2010, 9, 1)),
10708                            GregDay(-734_230, Date(-2010, 9, 30)),
10709                            GregDay(-734_229, Date(-2010, 10, 1)),
10710                            GregDay(-734_199, Date(-2010, 10, 31)),
10711                            GregDay(-734_198, Date(-2010, 11, 1)),
10712                            GregDay(-734_169, Date(-2010, 11, 30)),
10713                            GregDay(-734_168, Date(-2010, 12, 1)),
10714                            GregDay(-734_139, Date(-2010, 12, 30)),
10715                            GregDay(-734_138, Date(-2010, 12, 31)),
10716                            GregDay(-731_215, Date(-2001, 1, 1)),
10717                            GregDay(-730_850, Date(-2000, 1, 1)),
10718                            GregDay(-730_849, Date(-2000, 1, 2)),
10719                            GregDay(-730_486, Date(-2000, 12, 30)),
10720                            GregDay(-730_485, Date(-2000, 12, 31)),
10721                            GregDay(-730_484, Date(-1999, 1, 1)),
10722                            GregDay(-694_690, Date(-1901, 1, 1)),
10723                            GregDay(-694_325, Date(-1900, 1, 1)),
10724                            GregDay(-585_118, Date(-1601, 1, 1)),
10725                            GregDay(-584_753, Date(-1600, 1, 1)),
10726                            GregDay(-584_388, Date(-1600, 12, 31)),
10727                            GregDay(-584_387, Date(-1599, 1, 1)),
10728                            GregDay(-365_972, Date(-1001, 1, 1)),
10729                            GregDay(-365_607, Date(-1000, 1, 1)),
10730                            GregDay(-183_351, Date(-501, 1, 1)),
10731                            GregDay(-182_986, Date(-500, 1, 1)),
10732                            GregDay(-182_621, Date(-499, 1, 1)),
10733                            GregDay(-146_827, Date(-401, 1, 1)),
10734                            GregDay(-146_462, Date(-400, 1, 1)),
10735                            GregDay(-146_097, Date(-400, 12, 31)),
10736                            GregDay(-110_302, Date(-301, 1, 1)),
10737                            GregDay(-109_937, Date(-300, 1, 1)),
10738                            GregDay(-73_778, Date(-201, 1, 1)),
10739                            GregDay(-73_413, Date(-200, 1, 1)),
10740                            GregDay(-38_715, Date(-105, 1, 1)),
10741                            GregDay(-37_254, Date(-101, 1, 1)),
10742                            GregDay(-36_889, Date(-100, 1, 1)),
10743                            GregDay(-36_524, Date(-99, 1, 1)),
10744                            GregDay(-36_160, Date(-99, 12, 31)),
10745                            GregDay(-35_794, Date(-97, 1, 1)),
10746                            GregDay(-18_627, Date(-50, 1, 1)),
10747                            GregDay(-18_262, Date(-49, 1, 1)),
10748                            GregDay(-3652, Date(-9, 1, 1)),
10749                            GregDay(-2191, Date(-5, 1, 1)),
10750                            GregDay(-1827, Date(-5, 12, 31)),
10751                            GregDay(-1826, Date(-4, 1, 1)),
10752                            GregDay(-1825, Date(-4, 1, 2)),
10753                            GregDay(-1462, Date(-4, 12, 30)),
10754                            GregDay(-1461, Date(-4, 12, 31)),
10755                            GregDay(-1460, Date(-3, 1, 1)),
10756                            GregDay(-1096, Date(-3, 12, 31)),
10757                            GregDay(-1095, Date(-2, 1, 1)),
10758                            GregDay(-731, Date(-2, 12, 31)),
10759                            GregDay(-730, Date(-1, 1, 1)),
10760                            GregDay(-367, Date(-1, 12, 30)),
10761                            GregDay(-366, Date(-1, 12, 31)),
10762                            GregDay(-365, Date(0, 1, 1)),
10763                            GregDay(-31, Date(0, 11, 30)),
10764                            GregDay(-30, Date(0, 12, 1)),
10765                            GregDay(-1, Date(0, 12, 30)),
10766                            GregDay(0, Date(0, 12, 31))];
10767        return result;
10768     }
10769 
10770     GregDay[] testGregDaysAD()
10771     {
10772        static result = [GregDay(1, Date(1, 1, 1)),
10773                            GregDay(2, Date(1, 1, 2)),
10774                            GregDay(32, Date(1, 2, 1)),
10775                            GregDay(365, Date(1, 12, 31)),
10776                            GregDay(366, Date(2, 1, 1)),
10777                            GregDay(731, Date(3, 1, 1)),
10778                            GregDay(1096, Date(4, 1, 1)),
10779                            GregDay(1097, Date(4, 1, 2)),
10780                            GregDay(1460, Date(4, 12, 30)),
10781                            GregDay(1461, Date(4, 12, 31)),
10782                            GregDay(1462, Date(5, 1, 1)),
10783                            GregDay(17_898, Date(50, 1, 1)),
10784                            GregDay(35_065, Date(97, 1, 1)),
10785                            GregDay(36_160, Date(100, 1, 1)),
10786                            GregDay(36_525, Date(101, 1, 1)),
10787                            GregDay(37_986, Date(105, 1, 1)),
10788                            GregDay(72_684, Date(200, 1, 1)),
10789                            GregDay(73_049, Date(201, 1, 1)),
10790                            GregDay(109_208, Date(300, 1, 1)),
10791                            GregDay(109_573, Date(301, 1, 1)),
10792                            GregDay(145_732, Date(400, 1, 1)),
10793                            GregDay(146_098, Date(401, 1, 1)),
10794                            GregDay(182_257, Date(500, 1, 1)),
10795                            GregDay(182_622, Date(501, 1, 1)),
10796                            GregDay(364_878, Date(1000, 1, 1)),
10797                            GregDay(365_243, Date(1001, 1, 1)),
10798                            GregDay(584_023, Date(1600, 1, 1)),
10799                            GregDay(584_389, Date(1601, 1, 1)),
10800                            GregDay(693_596, Date(1900, 1, 1)),
10801                            GregDay(693_961, Date(1901, 1, 1)),
10802                            GregDay(729_755, Date(1999, 1, 1)),
10803                            GregDay(730_120, Date(2000, 1, 1)),
10804                            GregDay(730_121, Date(2000, 1, 2)),
10805                            GregDay(730_484, Date(2000, 12, 30)),
10806                            GregDay(730_485, Date(2000, 12, 31)),
10807                            GregDay(730_486, Date(2001, 1, 1)),
10808                            GregDay(733_773, Date(2010, 1, 1)),
10809                            GregDay(733_774, Date(2010, 1, 2)),
10810                            GregDay(733_803, Date(2010, 1, 31)),
10811                            GregDay(733_804, Date(2010, 2, 1)),
10812                            GregDay(733_831, Date(2010, 2, 28)),
10813                            GregDay(733_832, Date(2010, 3, 1)),
10814                            GregDay(733_862, Date(2010, 3, 31)),
10815                            GregDay(733_863, Date(2010, 4, 1)),
10816                            GregDay(733_892, Date(2010, 4, 30)),
10817                            GregDay(733_893, Date(2010, 5, 1)),
10818                            GregDay(733_923, Date(2010, 5, 31)),
10819                            GregDay(733_924, Date(2010, 6, 1)),
10820                            GregDay(733_953, Date(2010, 6, 30)),
10821                            GregDay(733_954, Date(2010, 7, 1)),
10822                            GregDay(733_984, Date(2010, 7, 31)),
10823                            GregDay(733_985, Date(2010, 8, 1)),
10824                            GregDay(734_015, Date(2010, 8, 31)),
10825                            GregDay(734_016, Date(2010, 9, 1)),
10826                            GregDay(734_045, Date(2010, 9, 30)),
10827                            GregDay(734_046, Date(2010, 10, 1)),
10828                            GregDay(734_076, Date(2010, 10, 31)),
10829                            GregDay(734_077, Date(2010, 11, 1)),
10830                            GregDay(734_106, Date(2010, 11, 30)),
10831                            GregDay(734_107, Date(2010, 12, 1)),
10832                            GregDay(734_136, Date(2010, 12, 30)),
10833                            GregDay(734_137, Date(2010, 12, 31)),
10834                            GregDay(734_503, Date(2012, 1, 1)),
10835                            GregDay(734_534, Date(2012, 2, 1)),
10836                            GregDay(734_561, Date(2012, 2, 28)),
10837                            GregDay(734_562, Date(2012, 2, 29)),
10838                            GregDay(734_563, Date(2012, 3, 1)),
10839                            GregDay(734_858, Date(2012, 12, 21))];
10840        return result;
10841     }
10842 
10843     // I'd use a Tuple, but I get forward reference errors if I try.
10844     struct DayOfYear { int day; MonthDay md; }
10845     DayOfYear[] testDaysOfYear()
10846     {
10847        static result = [DayOfYear(1, MonthDay(1, 1)),
10848                            DayOfYear(2, MonthDay(1, 2)),
10849                            DayOfYear(3, MonthDay(1, 3)),
10850                            DayOfYear(31, MonthDay(1, 31)),
10851                            DayOfYear(32, MonthDay(2, 1)),
10852                            DayOfYear(59, MonthDay(2, 28)),
10853                            DayOfYear(60, MonthDay(3, 1)),
10854                            DayOfYear(90, MonthDay(3, 31)),
10855                            DayOfYear(91, MonthDay(4, 1)),
10856                            DayOfYear(120, MonthDay(4, 30)),
10857                            DayOfYear(121, MonthDay(5, 1)),
10858                            DayOfYear(151, MonthDay(5, 31)),
10859                            DayOfYear(152, MonthDay(6, 1)),
10860                            DayOfYear(181, MonthDay(6, 30)),
10861                            DayOfYear(182, MonthDay(7, 1)),
10862                            DayOfYear(212, MonthDay(7, 31)),
10863                            DayOfYear(213, MonthDay(8, 1)),
10864                            DayOfYear(243, MonthDay(8, 31)),
10865                            DayOfYear(244, MonthDay(9, 1)),
10866                            DayOfYear(273, MonthDay(9, 30)),
10867                            DayOfYear(274, MonthDay(10, 1)),
10868                            DayOfYear(304, MonthDay(10, 31)),
10869                            DayOfYear(305, MonthDay(11, 1)),
10870                            DayOfYear(334, MonthDay(11, 30)),
10871                            DayOfYear(335, MonthDay(12, 1)),
10872                            DayOfYear(363, MonthDay(12, 29)),
10873                            DayOfYear(364, MonthDay(12, 30)),
10874                            DayOfYear(365, MonthDay(12, 31))];
10875        return result;
10876     }
10877 
10878     DayOfYear[] testDaysOfLeapYear()
10879     {
10880        static result = [DayOfYear(1, MonthDay(1, 1)),
10881                                DayOfYear(2, MonthDay(1, 2)),
10882                                DayOfYear(3, MonthDay(1, 3)),
10883                                DayOfYear(31, MonthDay(1, 31)),
10884                                DayOfYear(32, MonthDay(2, 1)),
10885                                DayOfYear(59, MonthDay(2, 28)),
10886                                DayOfYear(60, MonthDay(2, 29)),
10887                                DayOfYear(61, MonthDay(3, 1)),
10888                                DayOfYear(91, MonthDay(3, 31)),
10889                                DayOfYear(92, MonthDay(4, 1)),
10890                                DayOfYear(121, MonthDay(4, 30)),
10891                                DayOfYear(122, MonthDay(5, 1)),
10892                                DayOfYear(152, MonthDay(5, 31)),
10893                                DayOfYear(153, MonthDay(6, 1)),
10894                                DayOfYear(182, MonthDay(6, 30)),
10895                                DayOfYear(183, MonthDay(7, 1)),
10896                                DayOfYear(213, MonthDay(7, 31)),
10897                                DayOfYear(214, MonthDay(8, 1)),
10898                                DayOfYear(244, MonthDay(8, 31)),
10899                                DayOfYear(245, MonthDay(9, 1)),
10900                                DayOfYear(274, MonthDay(9, 30)),
10901                                DayOfYear(275, MonthDay(10, 1)),
10902                                DayOfYear(305, MonthDay(10, 31)),
10903                                DayOfYear(306, MonthDay(11, 1)),
10904                                DayOfYear(335, MonthDay(11, 30)),
10905                                DayOfYear(336, MonthDay(12, 1)),
10906                                DayOfYear(364, MonthDay(12, 29)),
10907                                DayOfYear(365, MonthDay(12, 30)),
10908                                DayOfYear(366, MonthDay(12, 31))];
10909        return result;
10910     }
10911 
10912     void initializeTests()
10913     {
10914         foreach (year; testYearsBC)
10915         {
10916             foreach (md; testMonthDays)
10917                 testDatesBC ~= Date(year, md.month, md.day);
10918         }
10919 
10920         foreach (year; testYearsAD)
10921         {
10922             foreach (md; testMonthDays)
10923                 testDatesAD ~= Date(year, md.month, md.day);
10924         }
10925 
10926         foreach (dt; testDatesBC)
10927         {
10928             foreach (tod; testTODs)
10929                 testDateTimesBC ~= DateTime(dt, tod);
10930         }
10931 
10932         foreach (dt; testDatesAD)
10933         {
10934             foreach (tod; testTODs)
10935                 testDateTimesAD ~= DateTime(dt, tod);
10936         }
10937     }
10938 }