1 // Written in the D programming language
2 /++
3 
4 $(SCRIPT inhibitQuickIndex = 1;)
5 $(DIVC quickindex,
6 $(BOOKTABLE,
7 $(TR $(TH Category) $(TH Functions))
8 $(TR $(TD Main date types) $(TD
9     $(LREF Date)
10     $(LREF DateTime)
11 ))
12 $(TR $(TD Other date types) $(TD
13     $(LREF Month)
14     $(LREF DayOfWeek)
15     $(LREF TimeOfDay)
16 ))
17 $(TR $(TD Date checking) $(TD
18     $(LREF valid)
19     $(LREF validTimeUnits)
20     $(LREF yearIsLeapYear)
21     $(LREF isTimePoint)
22     $(LREF enforceValid)
23 ))
24 $(TR $(TD Date conversion) $(TD
25     $(LREF daysToDayOfWeek)
26     $(LREF monthsToMonth)
27 ))
28 $(TR $(TD Time units) $(TD
29     $(LREF cmpTimeUnits)
30     $(LREF timeStrings)
31 ))
32 $(TR $(TD Other) $(TD
33     $(LREF AllowDayOverflow)
34     $(LREF DateTimeException)
35 ))
36 ))
37 
38     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
39     Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis)
40     Source:    $(PHOBOSSRC std/datetime/date.d)
41 +/
42 module std.datetime.date;
43 
44 import core.time : TimeException;
45 import std.traits : isSomeString, Unqual;
46 import std.typecons : Flag;
47 import std.range.primitives : isOutputRange;
48 
49 version (StdUnittest) import std.exception : assertThrown;
50 
51 @safe unittest
52 {
53     initializeTests();
54 }
55 
56 
57 /++
58     Exception type used by std.datetime. It's an alias to
59     $(REF TimeException,core,time). Either can be caught without concern about
60     which module it came from.
61   +/
62 alias DateTimeException = TimeException;
63 
64 
65 /++
66     Represents the 12 months of the Gregorian year (January is 1).
67   +/
68 enum Month : ubyte
69 {
70     jan = 1, ///
71     feb,     ///
72     mar,     ///
73     apr,     ///
74     may,     ///
75     jun,     ///
76     jul,     ///
77     aug,     ///
78     sep,     ///
79     oct,     ///
80     nov,     ///
81     dec      ///
82 }
83 
84 ///
85 @safe pure unittest
86 {
87     assert(Date(2018, 10, 1).month == Month.oct);
88     assert(DateTime(1, 1, 1).month == Month.jan);
89 }
90 
91 
92 /++
93     Represents the 7 days of the Gregorian week (Sunday is 0).
94   +/
95 enum DayOfWeek : ubyte
96 {
97     sun = 0, ///
98     mon,     ///
99     tue,     ///
100     wed,     ///
101     thu,     ///
102     fri,     ///
103     sat      ///
104 }
105 
106 ///
107 @safe pure unittest
108 {
109     assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
110     assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
111 }
112 
113 /++
114     In some date calculations, adding months or years can cause the date to fall
115     on a day of the month which is not valid (e.g. February 29th 2001 or
116     June 31st 2000). If overflow is allowed (as is the default), then the month
117     will be incremented accordingly (so, February 29th 2001 would become
118     March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
119     is not allowed, then the day will be adjusted to the last valid day in that
120     month (so, February 29th 2001 would become February 28th 2001 and
121     June 31st 2000 would become June 30th 2000).
122 
123     AllowDayOverflow only applies to calculations involving months or years.
124 
125     If set to `AllowDayOverflow.no`, then day overflow is not allowed.
126 
127     Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
128     allowed.
129   +/
130 alias AllowDayOverflow = Flag!"allowDayOverflow";
131 
132 
133 /++
134     Array of the strings representing time units, starting with the smallest
135     unit and going to the largest. It does not include `"nsecs"`.
136 
137     Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
138     `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
139     `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
140     `"years"`
141   +/
142 immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
143                                   "hours", "days", "weeks", "months", "years"];
144 
145 
146 /++
147     Combines the $(REF Date,std,datetime,date) and
148     $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
149     both the date and the time. It is optimized for calendar-based operations
150     and has no concept of time zone. For an object which is optimized for time
151     operations based on the system time, use
152     $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
153     a concept of time zone and has much higher precision (hnsecs). `DateTime`
154     is intended primarily for calendar-based uses rather than precise time
155     operations.
156   +/
157 struct DateTime
158 {
159 public:
160 
161     /++
162         Params:
163             date = The date portion of $(LREF DateTime).
164             tod  = The time portion of $(LREF DateTime).
165       +/
166     this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
167     {
168         _date = date;
169         _tod = tod;
170     }
171 
172     @safe unittest
173     {
174         {
175             auto dt = DateTime.init;
176             assert(dt._date == Date.init);
177             assert(dt._tod == TimeOfDay.init);
178         }
179 
180         {
181             auto dt = DateTime(Date(1999, 7 ,6));
182             assert(dt._date == Date(1999, 7, 6));
183             assert(dt._tod == TimeOfDay.init);
184         }
185 
186         {
187             auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33));
188             assert(dt._date == Date(1999, 7, 6));
189             assert(dt._tod == TimeOfDay(12, 30, 33));
190         }
191     }
192 
193 
194     /++
195         Params:
196             year   = The year portion of the date.
197             month  = The month portion of the date (January is 1).
198             day    = The day portion of the date.
199             hour   = The hour portion of the time;
200             minute = The minute portion of the time;
201             second = The second portion of the time;
202       +/
203     this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure
204     {
205         _date = Date(year, month, day);
206         _tod = TimeOfDay(hour, minute, second);
207     }
208 
209     @safe unittest
210     {
211         {
212             auto dt = DateTime(1999, 7 ,6);
213             assert(dt._date == Date(1999, 7, 6));
214             assert(dt._tod == TimeOfDay.init);
215         }
216 
217         {
218             auto dt = DateTime(1999, 7 ,6, 12, 30, 33);
219             assert(dt._date == Date(1999, 7, 6));
220             assert(dt._tod == TimeOfDay(12, 30, 33));
221         }
222     }
223 
224 
225     /++
226         Compares this $(LREF DateTime) with the given `DateTime.`.
227 
228         Returns:
229             $(BOOKTABLE,
230             $(TR $(TD this < rhs) $(TD < 0))
231             $(TR $(TD this == rhs) $(TD 0))
232             $(TR $(TD this > rhs) $(TD > 0))
233             )
234      +/
235     int opCmp(DateTime rhs) const @safe pure nothrow @nogc
236     {
237         immutable dateResult = _date.opCmp(rhs._date);
238 
239         if (dateResult != 0)
240             return dateResult;
241 
242         return _tod.opCmp(rhs._tod);
243     }
244 
245     @safe unittest
246     {
247         // Test A.D.
248         assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0);
249 
250         assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0);
251         assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0);
252         assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0);
253 
254         assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0);
255         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0);
256 
257         assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0);
258 
259         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
260         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
261         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
262         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
263         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0);
264         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
265 
266         assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
267         assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
268         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
269         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
270         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
271         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
272 
273 
274         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp(
275                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0);
276         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp(
277                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0);
278         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp(
279                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0);
280         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
281                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
282 
283         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp(
284                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0);
285         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
286                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
287 
288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp(
289                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0);
290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
291                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
292 
293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
294                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
296                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
298                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
300                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
302                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0);
303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
304                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
305 
306         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
307                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
308         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
309                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
310         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
311                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
312         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
313                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
314 
315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
316                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
317         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
318                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
319 
320         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
321                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
322         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
323                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
324         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
325                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
326         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
327                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
328         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
329                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
330         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
331                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
332 
333         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
334                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
335         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
336                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
337         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
338                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
339         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
340                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
341         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
342                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
343         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
344                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
345 
346         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
347                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
348         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
349                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
350         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
351                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0);
352         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
353                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
354         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
355                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
356         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
357                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
358 
359         // Test B.C.
360         assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp(
361                    DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0);
362         assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
363                    DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0);
364         assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp(
365                    DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0);
366 
367         assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
368                    DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0);
369         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
370                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
371 
372         assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
373                    DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0);
374 
375         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
376                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
377         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
378                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
379         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
380                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
381         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
382                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
383         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
384                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
385         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
386                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
387 
388         assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
389                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
390         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
391                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
392         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
393                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
394         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
395                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
396         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
397                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
398         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
399                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
400 
401         // Test Both
402         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
403                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
404         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
405                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
406 
407         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
408                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
409         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
410                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0);
411 
412         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
413                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
414         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
415                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
416 
417         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
418                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
419         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
420                    DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0);
421 
422         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
423                    DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0);
424         assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp(
425                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
426 
427         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
428         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
429         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
430         assert(dt.opCmp(dt) == 0);
431         assert(dt.opCmp(cdt) == 0);
432         assert(dt.opCmp(idt) == 0);
433         assert(cdt.opCmp(dt) == 0);
434         assert(cdt.opCmp(cdt) == 0);
435         assert(cdt.opCmp(idt) == 0);
436         assert(idt.opCmp(dt) == 0);
437         assert(idt.opCmp(cdt) == 0);
438         assert(idt.opCmp(idt) == 0);
439     }
440 
441 
442     /++
443         The date portion of $(LREF DateTime).
444       +/
445     @property Date date() const @safe pure nothrow @nogc
446     {
447         return _date;
448     }
449 
450     @safe unittest
451     {
452         {
453             auto dt = DateTime.init;
454             assert(dt.date == Date.init);
455         }
456 
457         {
458             auto dt = DateTime(Date(1999, 7, 6));
459             assert(dt.date == Date(1999, 7, 6));
460         }
461 
462         const cdt = DateTime(1999, 7, 6);
463         immutable idt = DateTime(1999, 7, 6);
464         assert(cdt.date == Date(1999, 7, 6));
465         assert(idt.date == Date(1999, 7, 6));
466     }
467 
468 
469     /++
470         The date portion of $(LREF DateTime).
471 
472         Params:
473             date = The Date to set this $(LREF DateTime)'s date portion to.
474       +/
475     @property void date(Date date) @safe pure nothrow @nogc
476     {
477         _date = date;
478     }
479 
480     @safe unittest
481     {
482         auto dt = DateTime.init;
483         dt.date = Date(1999, 7, 6);
484         assert(dt._date == Date(1999, 7, 6));
485         assert(dt._tod == TimeOfDay.init);
486 
487         const cdt = DateTime(1999, 7, 6);
488         immutable idt = DateTime(1999, 7, 6);
489         static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1)));
490         static assert(!__traits(compiles, idt.date = Date(2010, 1, 1)));
491     }
492 
493 
494     /++
495         The time portion of $(LREF DateTime).
496       +/
497     @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
498     {
499         return _tod;
500     }
501 
502     @safe unittest
503     {
504         {
505             auto dt = DateTime.init;
506             assert(dt.timeOfDay == TimeOfDay.init);
507         }
508 
509         {
510             auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33));
511             assert(dt.timeOfDay == TimeOfDay(12, 30, 33));
512         }
513 
514         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
515         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
516         assert(cdt.timeOfDay == TimeOfDay(12, 30, 33));
517         assert(idt.timeOfDay == TimeOfDay(12, 30, 33));
518     }
519 
520 
521     /++
522         The time portion of $(LREF DateTime).
523 
524         Params:
525             tod = The $(REF TimeOfDay,std,datetime,date) to set this
526                   $(LREF DateTime)'s time portion to.
527       +/
528     @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
529     {
530         _tod = tod;
531     }
532 
533     @safe unittest
534     {
535         auto dt = DateTime.init;
536         dt.timeOfDay = TimeOfDay(12, 30, 33);
537         assert(dt._date == Date.init);
538         assert(dt._tod == TimeOfDay(12, 30, 33));
539 
540         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
541         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
542         static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33)));
543         static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33)));
544     }
545 
546 
547     /++
548         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
549         are B.C.
550      +/
551     @property short year() const @safe pure nothrow @nogc
552     {
553         return _date.year;
554     }
555 
556     @safe unittest
557     {
558         assert(Date.init.year == 1);
559         assert(Date(1999, 7, 6).year == 1999);
560         assert(Date(-1999, 7, 6).year == -1999);
561 
562         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
563         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
564         assert(idt.year == 1999);
565         assert(idt.year == 1999);
566     }
567 
568 
569     /++
570         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
571         are B.C.
572 
573         Params:
574             year = The year to set this $(LREF DateTime)'s year to.
575 
576         Throws:
577             $(REF DateTimeException,std,datetime,date) if the new year is not
578             a leap year and if the resulting date would be on February 29th.
579      +/
580     @property void year(int year) @safe pure
581     {
582         _date.year = year;
583     }
584 
585     ///
586     @safe unittest
587     {
588         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
589         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
590         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
591     }
592 
593     @safe unittest
594     {
595         static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
596         {
597             dt.year = year;
598             assert(dt == expected);
599         }
600 
601         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
602                1999,
603                DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)));
604         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
605                0,
606                DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)));
607         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
608                -1999,
609                DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)));
610 
611         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
612         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
613         static assert(!__traits(compiles, cdt.year = 7));
614         static assert(!__traits(compiles, idt.year = 7));
615     }
616 
617 
618     /++
619         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
620 
621         Throws:
622             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
623      +/
624     @property short yearBC() const @safe pure
625     {
626         return _date.yearBC;
627     }
628 
629     ///
630     @safe unittest
631     {
632         assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
633         assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
634         assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
635     }
636 
637     @safe unittest
638     {
639         assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
640 
641         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
642         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
643         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
644         dt.yearBC = 12;
645         assert(dt.yearBC == 12);
646         static assert(!__traits(compiles, cdt.yearBC = 12));
647         static assert(!__traits(compiles, idt.yearBC = 12));
648     }
649 
650 
651     /++
652         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
653 
654         Params:
655             year = The year B.C. to set this $(LREF DateTime)'s year to.
656 
657         Throws:
658             $(REF DateTimeException,std,datetime,date) if a non-positive value
659             is given.
660      +/
661     @property void yearBC(int year) @safe pure
662     {
663         _date.yearBC = year;
664     }
665 
666     ///
667     @safe unittest
668     {
669         auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
670         dt.yearBC = 1;
671         assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
672 
673         dt.yearBC = 10;
674         assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
675     }
676 
677     @safe unittest
678     {
679         assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1))));
680 
681         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
682         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
683         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
684         dt.yearBC = 12;
685         assert(dt.yearBC == 12);
686         static assert(!__traits(compiles, cdt.yearBC = 12));
687         static assert(!__traits(compiles, idt.yearBC = 12));
688     }
689 
690 
691     /++
692         Month of a Gregorian Year.
693      +/
694     @property Month month() const @safe pure nothrow @nogc
695     {
696         return _date.month;
697     }
698 
699     ///
700     @safe unittest
701     {
702         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
703         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
704         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
705     }
706 
707     @safe unittest
708     {
709         assert(DateTime.init.month == 1);
710         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
711         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
712 
713         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
714         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
715         assert(cdt.month == 7);
716         assert(idt.month == 7);
717     }
718 
719 
720     /++
721         Month of a Gregorian Year.
722 
723         Params:
724             month = The month to set this $(LREF DateTime)'s month to.
725 
726         Throws:
727             $(REF DateTimeException,std,datetime,date) if the given month is
728             not a valid month.
729      +/
730     @property void month(Month month) @safe pure
731     {
732         _date.month = month;
733     }
734 
735     @safe unittest
736     {
737         static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
738         {
739             dt.month = month;
740             assert(expected != DateTime.init);
741             assert(dt == expected);
742         }
743 
744         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0));
745         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13));
746 
747         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
748                cast(Month) 7,
749                DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)));
750         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)),
751                cast(Month) 7,
752                DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)));
753 
754         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
755         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
756         static assert(!__traits(compiles, cdt.month = 12));
757         static assert(!__traits(compiles, idt.month = 12));
758     }
759 
760 
761     /++
762         Day of a Gregorian Month.
763      +/
764     @property ubyte day() const @safe pure nothrow @nogc
765     {
766         return _date.day;
767     }
768 
769     ///
770     @safe unittest
771     {
772         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
773         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
774         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
775     }
776 
777     @safe unittest
778     {
779         import std.format : format;
780         import std.range : chain;
781 
782         static void test(DateTime dateTime, int expected)
783         {
784             assert(dateTime.day == expected, format("Value given: %s", dateTime));
785         }
786 
787         foreach (year; chain(testYearsBC, testYearsAD))
788         {
789             foreach (md; testMonthDays)
790             {
791                 foreach (tod; testTODs)
792                     test(DateTime(Date(year, md.month, md.day), tod), md.day);
793             }
794         }
795 
796         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
797         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
798         assert(cdt.day == 6);
799         assert(idt.day == 6);
800     }
801 
802 
803     /++
804         Day of a Gregorian Month.
805 
806         Params:
807             day = The day of the month to set this $(LREF DateTime)'s day to.
808 
809         Throws:
810             $(REF DateTimeException,std,datetime,date) if the given day is not
811             a valid day of the current month.
812      +/
813     @property void day(int day) @safe pure
814     {
815         _date.day = day;
816     }
817 
818     @safe unittest
819     {
820         import std.exception : assertNotThrown;
821 
822         static void testDT(DateTime dt, int day)
823         {
824             dt.day = day;
825         }
826 
827         // Test A.D.
828         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0));
829         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32));
830         assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29));
831         assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30));
832         assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32));
833         assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31));
834         assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32));
835         assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31));
836         assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32));
837         assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32));
838         assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31));
839         assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32));
840         assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31));
841         assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32));
842 
843         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31));
844         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28));
845         assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29));
846         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31));
847         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30));
848         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31));
849         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30));
850         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31));
851         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31));
852         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30));
853         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31));
854         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30));
855         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31));
856 
857         {
858             auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22));
859             dt.day = 6;
860             assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22)));
861         }
862 
863         // Test B.C.
864         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0));
865         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32));
866         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29));
867         assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30));
868         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32));
869         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31));
870         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32));
871         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31));
872         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32));
873         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32));
874         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31));
875         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32));
876         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31));
877         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32));
878 
879         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31));
880         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28));
881         assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29));
882         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31));
883         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30));
884         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31));
885         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30));
886         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31));
887         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31));
888         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30));
889         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31));
890         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30));
891         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31));
892 
893         auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22));
894         dt.day = 6;
895         assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22)));
896 
897         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
898         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
899         static assert(!__traits(compiles, cdt.day = 27));
900         static assert(!__traits(compiles, idt.day = 27));
901     }
902 
903 
904     /++
905         Hours past midnight.
906      +/
907     @property ubyte hour() const @safe pure nothrow @nogc
908     {
909         return _tod.hour;
910     }
911 
912     @safe unittest
913     {
914         assert(DateTime.init.hour == 0);
915         assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12);
916 
917         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
918         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
919         assert(cdt.hour == 12);
920         assert(idt.hour == 12);
921     }
922 
923 
924     /++
925         Hours past midnight.
926 
927         Params:
928             hour = The hour of the day to set this $(LREF DateTime)'s hour to.
929 
930         Throws:
931             $(REF DateTimeException,std,datetime,date) if the given hour would
932             result in an invalid $(LREF DateTime).
933      +/
934     @property void hour(int hour) @safe pure
935     {
936         _tod.hour = hour;
937     }
938 
939     @safe unittest
940     {
941         assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}());
942 
943         auto dt = DateTime.init;
944         dt.hour = 12;
945         assert(dt == DateTime(1, 1, 1, 12, 0, 0));
946 
947         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
948         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
949         static assert(!__traits(compiles, cdt.hour = 27));
950         static assert(!__traits(compiles, idt.hour = 27));
951     }
952 
953 
954     /++
955         Minutes past the hour.
956      +/
957     @property ubyte minute() const @safe pure nothrow @nogc
958     {
959         return _tod.minute;
960     }
961 
962     @safe unittest
963     {
964         assert(DateTime.init.minute == 0);
965         assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30);
966 
967         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
968         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
969         assert(cdt.minute == 30);
970         assert(idt.minute == 30);
971     }
972 
973 
974     /++
975         Minutes past the hour.
976 
977         Params:
978             minute = The minute to set this $(LREF DateTime)'s minute to.
979 
980         Throws:
981             $(REF DateTimeException,std,datetime,date) if the given minute
982             would result in an invalid $(LREF DateTime).
983      +/
984     @property void minute(int minute) @safe pure
985     {
986         _tod.minute = minute;
987     }
988 
989     @safe unittest
990     {
991         assertThrown!DateTimeException((){DateTime.init.minute = 60;}());
992 
993         auto dt = DateTime.init;
994         dt.minute = 30;
995         assert(dt == DateTime(1, 1, 1, 0, 30, 0));
996 
997         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
998         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
999         static assert(!__traits(compiles, cdt.minute = 27));
1000         static assert(!__traits(compiles, idt.minute = 27));
1001     }
1002 
1003 
1004     /++
1005         Seconds past the minute.
1006      +/
1007     @property ubyte second() const @safe pure nothrow @nogc
1008     {
1009         return _tod.second;
1010     }
1011 
1012     @safe unittest
1013     {
1014         assert(DateTime.init.second == 0);
1015         assert(DateTime(1, 1, 1, 0, 0, 33).second == 33);
1016 
1017         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1018         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1019         assert(cdt.second == 33);
1020         assert(idt.second == 33);
1021     }
1022 
1023 
1024     /++
1025         Seconds past the minute.
1026 
1027         Params:
1028             second = The second to set this $(LREF DateTime)'s second to.
1029 
1030         Throws:
1031             $(REF DateTimeException,std,datetime,date) if the given seconds
1032             would result in an invalid $(LREF DateTime).
1033      +/
1034     @property void second(int second) @safe pure
1035     {
1036         _tod.second = second;
1037     }
1038 
1039     @safe unittest
1040     {
1041         assertThrown!DateTimeException((){DateTime.init.second = 60;}());
1042 
1043         auto dt = DateTime.init;
1044         dt.second = 33;
1045         assert(dt == DateTime(1, 1, 1, 0, 0, 33));
1046 
1047         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1048         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1049         static assert(!__traits(compiles, cdt.second = 27));
1050         static assert(!__traits(compiles, idt.second = 27));
1051     }
1052 
1053 
1054     /++
1055         Adds the given number of years or months to this $(LREF DateTime),
1056         mutating it. A negative number will subtract.
1057 
1058         Note that if day overflow is allowed, and the date with the adjusted
1059         year/month overflows the number of days in the new month, then the month
1060         will be incremented by one, and the day set to the number of days
1061         overflowed. (e.g. if the day were 31 and the new month were June, then
1062         the month would be incremented to July, and the new day would be 1). If
1063         day overflow is not allowed, then the day will be set to the last valid
1064         day in the month (e.g. June 31st would become June 30th).
1065 
1066         Params:
1067             units         = The type of units to add ("years" or "months").
1068             value         = The number of months or years to add to this
1069                             $(LREF DateTime).
1070             allowOverflow = Whether the days should be allowed to overflow,
1071                             causing the month to increment.
1072 
1073         Returns:
1074             A reference to the `DateTime` (`this`).
1075       +/
1076     ref DateTime add(string units)
1077                     (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1078         if (units == "years" || units == "months")
1079     {
1080         _date.add!units(value, allowOverflow);
1081         return this;
1082     }
1083 
1084     ///
1085     @safe unittest
1086     {
1087         auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
1088         dt1.add!"months"(11);
1089         assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
1090 
1091         auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
1092         dt2.add!"months"(-11);
1093         assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
1094 
1095         auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
1096         dt3.add!"years"(1);
1097         assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
1098 
1099         auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
1100         dt4.add!"years"(1, AllowDayOverflow.no);
1101         assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
1102     }
1103 
1104     @safe unittest
1105     {
1106         auto dt = DateTime(2000, 1, 31);
1107         dt.add!"years"(7).add!"months"(-4);
1108         assert(dt == DateTime(2006, 10, 1));
1109 
1110         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1111         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1112         static assert(!__traits(compiles, cdt.add!"years"(4)));
1113         static assert(!__traits(compiles, idt.add!"years"(4)));
1114         static assert(!__traits(compiles, cdt.add!"months"(4)));
1115         static assert(!__traits(compiles, idt.add!"months"(4)));
1116     }
1117 
1118 
1119     /++
1120         Adds the given number of years or months to this $(LREF DateTime),
1121         mutating it. A negative number will subtract.
1122 
1123         The difference between rolling and adding is that rolling does not
1124         affect larger units. Rolling a $(LREF DateTime) 12 months
1125         gets the exact same $(LREF DateTime). However, the days can still be
1126         affected due to the differing number of days in each month.
1127 
1128         Because there are no units larger than years, there is no difference
1129         between adding and rolling years.
1130 
1131         Params:
1132             units         = The type of units to add ("years" or "months").
1133             value         = The number of months or years to add to this
1134                             $(LREF DateTime).
1135             allowOverflow = Whether the days should be allowed to overflow,
1136                             causing the month to increment.
1137 
1138         Returns:
1139             A reference to the `DateTime` (`this`).
1140       +/
1141     ref DateTime roll(string units)
1142                      (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1143         if (units == "years" || units == "months")
1144     {
1145         _date.roll!units(value, allowOverflow);
1146         return this;
1147     }
1148 
1149     ///
1150     @safe unittest
1151     {
1152         auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
1153         dt1.roll!"months"(1);
1154         assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
1155 
1156         auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
1157         dt2.roll!"months"(-1);
1158         assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
1159 
1160         auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
1161         dt3.roll!"months"(1);
1162         assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
1163 
1164         auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
1165         dt4.roll!"months"(1, AllowDayOverflow.no);
1166         assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
1167 
1168         auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
1169         dt5.roll!"years"(1);
1170         assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
1171 
1172         auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
1173         dt6.roll!"years"(1, AllowDayOverflow.no);
1174         assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
1175     }
1176 
1177     @safe unittest
1178     {
1179         auto dt = DateTime(2000, 1, 31);
1180         dt.roll!"years"(7).roll!"months"(-4);
1181         assert(dt == DateTime(2007, 10, 1));
1182 
1183         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1184         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1185         static assert(!__traits(compiles, cdt.roll!"years"(4)));
1186         static assert(!__traits(compiles, idt.roll!"years"(4)));
1187         static assert(!__traits(compiles, cdt.roll!"months"(4)));
1188         static assert(!__traits(compiles, idt.roll!"months"(4)));
1189     }
1190 
1191 
1192     /++
1193         Adds the given number of units to this $(LREF DateTime), mutating it. A
1194         negative number will subtract.
1195 
1196         The difference between rolling and adding is that rolling does not
1197         affect larger units. For instance, rolling a $(LREF DateTime) one
1198         year's worth of days gets the exact same $(LREF DateTime).
1199 
1200         Accepted units are `"days"`, `"minutes"`, `"hours"`,
1201         `"minutes"`, and `"seconds"`.
1202 
1203         Params:
1204             units = The units to add.
1205             value = The number of $(D_PARAM units) to add to this
1206                     $(LREF DateTime).
1207 
1208         Returns:
1209             A reference to the `DateTime` (`this`).
1210       +/
1211     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1212         if (units == "days")
1213     {
1214         _date.roll!"days"(value);
1215         return this;
1216     }
1217 
1218     ///
1219     @safe unittest
1220     {
1221         auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
1222         dt1.roll!"days"(1);
1223         assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
1224         dt1.roll!"days"(365);
1225         assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
1226         dt1.roll!"days"(-32);
1227         assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
1228 
1229         auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
1230         dt2.roll!"hours"(1);
1231         assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
1232 
1233         auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
1234         dt3.roll!"seconds"(-1);
1235         assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
1236     }
1237 
1238     @safe unittest
1239     {
1240         auto dt = DateTime(2000, 1, 31);
1241         dt.roll!"days"(7).roll!"days"(-4);
1242         assert(dt == DateTime(2000, 1, 3));
1243 
1244         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1245         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1246         static assert(!__traits(compiles, cdt.roll!"days"(4)));
1247         static assert(!__traits(compiles, idt.roll!"days"(4)));
1248     }
1249 
1250 
1251     /// ditto
1252     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1253         if (units == "hours" ||
1254             units == "minutes" ||
1255             units == "seconds")
1256     {
1257         _tod.roll!units(value);
1258         return this;
1259     }
1260 
1261     // Test roll!"hours"().
1262     @safe unittest
1263     {
1264         static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
1265         {
1266             orig.roll!"hours"(hours);
1267             assert(orig == expected);
1268         }
1269 
1270         // Test A.D.
1271         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1273         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1274                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1275         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1276                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1277         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1278                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1279         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1280                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1281         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1282                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1283         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1284                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1285         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1286                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1287         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1288                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1289         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1290                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1291         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1292                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1293         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1294                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1295         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1296                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1297         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1298                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1299         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1300                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1301         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1302                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1303         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1304                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1305         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1306                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1307         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1308                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1309         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1310                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1311         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1312                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1313         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1314                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1315         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1316                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1317         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1318                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1319         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1320                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1321         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1322                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1323 
1324         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1325                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1326         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1327                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1328         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1329                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1330         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1331                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1332         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1333                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1334         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1335                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1336         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1337                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1338         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1339                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1340         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1341                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1342         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1343                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1344         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1345                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1346         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1347                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1348         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1349                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1350         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1351                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1352         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1353                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1354         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1355                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1356         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1357                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1358         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1359                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1360         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1361                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1362         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1363                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1364         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1365                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1366         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1367                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1368         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1369                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1370         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1371                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1372         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1373                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1374 
1375         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1376                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1377         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1378                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1379         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1380                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1381 
1382         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1383                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1384         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1385                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1386         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1387                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1388 
1389         testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1390                DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33)));
1391         testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1392                DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33)));
1393 
1394         testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1,
1395                DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33)));
1396         testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1397                DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33)));
1398 
1399         testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25,
1400                DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33)));
1401         testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25,
1402                DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33)));
1403 
1404         testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1405                DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33)));
1406         testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1407                DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33)));
1408 
1409         // Test B.C.
1410         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1411                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1412         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1413                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1414         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1415                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1416         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1417                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1418         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1419                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1420         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1421                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1422         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1423                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1424         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1425                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1426         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1427                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1428         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1429                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1430         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1431                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1432         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1433                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1434         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1435                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1436         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1437                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1438         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1439                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1440         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1441                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1442         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1443                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1444         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1445                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1446         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1447                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1448         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1449                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1450         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1451                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1452         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1453                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1454         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1455                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1456         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1457                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1458         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1459                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1460         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1461                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1462 
1463         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1464                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1465         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1466                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1467         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1468                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1469         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1470                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1471         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1472                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1473         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1474                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1475         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1476                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1477         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1478                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1479         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1480                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1481         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1482                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1483         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1484                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1485         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1486                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1487         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1488                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1489         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1490                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1491         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1492                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1493         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1494                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1495         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1496                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1497         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1498                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1499         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1500                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1501         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1502                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1503         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1504                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1505         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1506                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1507         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1508                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1509         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1510                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1511         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1512                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1513 
1514         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1515                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1516         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1517                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1518         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1519                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1520 
1521         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1522                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1523         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1524                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1525         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1526                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1527 
1528         testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1529                DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33)));
1530         testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1531                DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33)));
1532 
1533         testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1,
1534                DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33)));
1535         testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1536                DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33)));
1537 
1538         testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25,
1539                DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33)));
1540         testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25,
1541                DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33)));
1542 
1543         testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1544                DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33)));
1545         testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1546                DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33)));
1547 
1548         // Test Both
1549         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546,
1550                DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33)));
1551         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546,
1552                DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33)));
1553 
1554         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1555         dt.roll!"hours"(27).roll!"hours"(-9);
1556         assert(dt == DateTime(2000, 1, 31, 3, 7, 6));
1557 
1558         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1559         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1560         static assert(!__traits(compiles, cdt.roll!"hours"(4)));
1561         static assert(!__traits(compiles, idt.roll!"hours"(4)));
1562     }
1563 
1564     // Test roll!"minutes"().
1565     @safe unittest
1566     {
1567         static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
1568         {
1569             orig.roll!"minutes"(minutes);
1570             assert(orig == expected);
1571         }
1572 
1573         // Test A.D.
1574         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1575                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1576         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1577                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1578         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1579                DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33)));
1580         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1581                DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33)));
1582         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1583                DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33)));
1584         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1585                DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33)));
1586         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1587                DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33)));
1588         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1589                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1590         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1591                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1592         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1593                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1594         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1595                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1596         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1597                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1598         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1599                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1600         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1601                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1602         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1603                DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33)));
1604 
1605         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1606                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1607         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1608                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1609         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1610                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1611         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1612                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1613         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1614                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1615         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1616                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1617         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1618                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1619         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1620                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1621 
1622         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1623                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1624         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1625                DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33)));
1626         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1627                DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33)));
1628         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1629                DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33)));
1630         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1631                DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33)));
1632         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1633                DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33)));
1634         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1635                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1636         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1637                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1638         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1639                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1640         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1641                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1642         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1643                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1644         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1645                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1646         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1647                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1648         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1649                DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33)));
1650 
1651         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1652                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1653         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1654                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1655         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1656                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1657         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1658                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1659         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1660                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1661         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1662                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1663         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1664                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1665         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1666                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1667 
1668         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1669                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1670         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1671                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1672         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1673                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1674 
1675         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1676                DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33)));
1677         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1678                DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)));
1679         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1680                DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33)));
1681 
1682         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1683                DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33)));
1684         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1685                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)));
1686         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1687                DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33)));
1688 
1689         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1690                DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33)));
1691         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1692                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)));
1693         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1694                DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33)));
1695 
1696         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1,
1697                DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33)));
1698         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0,
1699                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)));
1700         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1,
1701                DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33)));
1702 
1703         // Test B.C.
1704         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1705                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1706         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1707                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1708         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1709                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33)));
1710         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1711                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33)));
1712         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1713                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33)));
1714         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1715                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33)));
1716         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1717                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33)));
1718         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1719                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1720         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1721                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1722         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1723                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1724         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1725                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1726         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1727                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1728         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1729                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1730         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1731                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1732         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1733                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33)));
1734 
1735         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1736                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1737         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1738                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1739         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1740                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1741         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1742                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1743         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1744                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1745         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1746                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1747         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1748                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1749         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1750                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1751 
1752         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1753                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1754         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1755                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33)));
1756         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1757                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33)));
1758         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1759                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33)));
1760         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1761                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33)));
1762         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1763                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33)));
1764         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1765                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1766         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1767                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1768         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1769                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1770         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1771                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1772         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1773                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1774         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1775                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1776         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1777                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1778         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1779                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33)));
1780 
1781         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1782                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1783         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1784                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1785         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1786                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1787         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1788                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1789         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1790                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1791         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1792                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1793         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1794                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1795         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1796                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1797 
1798         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1799                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1800         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1801                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1802         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1803                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1804 
1805         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1806                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33)));
1807         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1808                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)));
1809         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1810                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33)));
1811 
1812         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1813                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33)));
1814         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1815                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)));
1816         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1817                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33)));
1818 
1819         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1820                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33)));
1821         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1822                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)));
1823         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1824                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33)));
1825 
1826         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1,
1827                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33)));
1828         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0,
1829                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)));
1830         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1,
1831                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33)));
1832 
1833         // Test Both
1834         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
1835                DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0)));
1836         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1,
1837                DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0)));
1838 
1839         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
1840                DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0)));
1841         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1,
1842                DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0)));
1843 
1844         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760,
1845                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
1846         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760,
1847                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1848 
1849         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782,
1850                DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33)));
1851         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782,
1852                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1853 
1854         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1855         dt.roll!"minutes"(92).roll!"minutes"(-292);
1856         assert(dt == DateTime(2000, 1, 31, 9, 47, 6));
1857 
1858         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1859         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1860         static assert(!__traits(compiles, cdt.roll!"minutes"(4)));
1861         static assert(!__traits(compiles, idt.roll!"minutes"(4)));
1862     }
1863 
1864     // Test roll!"seconds"().
1865     @safe unittest
1866     {
1867         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
1868         {
1869             orig.roll!"seconds"(seconds);
1870             assert(orig == expected);
1871         }
1872 
1873         // Test A.D.
1874         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1875                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1876         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1877                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1878         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1879                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35)));
1880         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1881                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36)));
1882         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1883                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37)));
1884         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1885                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38)));
1886         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1887                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43)));
1888         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1889                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48)));
1890         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
1891                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1892         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
1893                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1894         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1895                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3)));
1896         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
1897                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1898         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1899                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1900         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
1901                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1902 
1903         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
1904                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1905         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
1906                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1907         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
1908                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1909         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
1910                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1911         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
1912                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1913         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
1914                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1915         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
1916                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1917         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
1918                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1919 
1920         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1921                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1922         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1923                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31)));
1924         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1925                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30)));
1926         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1927                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29)));
1928         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1929                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28)));
1930         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1931                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23)));
1932         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1933                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18)));
1934         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
1935                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1936         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
1937                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1938         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
1939                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58)));
1940         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
1941                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1942         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1943                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1944         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
1945                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1946 
1947         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
1948                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1949         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
1950                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1951         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
1952                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1953 
1954         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
1955                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1)));
1956         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
1957                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)));
1958         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
1959                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59)));
1960 
1961         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
1962                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1)));
1963         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
1964                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)));
1965         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
1966                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59)));
1967 
1968         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
1969                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0)));
1970         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
1971                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)));
1972         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
1973                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58)));
1974 
1975         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1,
1976                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0)));
1977         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0,
1978                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)));
1979         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1,
1980                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58)));
1981 
1982         // Test B.C.
1983         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1984                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1985         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1986                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1987         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1988                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35)));
1989         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1990                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36)));
1991         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1992                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37)));
1993         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1994                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38)));
1995         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1996                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43)));
1997         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1998                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48)));
1999         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
2000                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2001         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
2002                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2003         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
2004                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3)));
2005         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
2006                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2007         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
2008                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2009         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
2010                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2011 
2012         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
2013                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2014         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
2015                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2016         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
2017                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2018         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
2019                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2020         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
2021                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2022         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
2023                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2024         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
2025                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2026         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
2027                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2028 
2029         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
2030                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2031         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
2032                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31)));
2033         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
2034                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30)));
2035         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
2036                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29)));
2037         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
2038                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28)));
2039         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
2040                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23)));
2041         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
2042                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18)));
2043         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
2044                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2045         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
2046                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2047         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
2048                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58)));
2049         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
2050                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2051         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
2052                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2053         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
2054                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2055 
2056         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
2057                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2058         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
2059                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2060         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
2061                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2062 
2063         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
2064                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1)));
2065         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
2066                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)));
2067         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
2068                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59)));
2069 
2070         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
2071                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1)));
2072         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
2073                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)));
2074         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
2075                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59)));
2076 
2077         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
2078                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0)));
2079         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
2080                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)));
2081         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
2082                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58)));
2083 
2084         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1,
2085                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0)));
2086         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0,
2087                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)));
2088         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1,
2089                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58)));
2090 
2091         // Test Both
2092         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
2093                DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59)));
2094         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1,
2095                DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)));
2096 
2097         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
2098                DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59)));
2099         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1,
2100                DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)));
2101 
2102         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L,
2103                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
2104         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L,
2105                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2106 
2107         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L,
2108                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50)));
2109         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L,
2110                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2111 
2112         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2113         dt.roll!"seconds"(92).roll!"seconds"(-292);
2114         assert(dt == DateTime(2000, 1, 31, 9, 7, 46));
2115 
2116         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2117         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2118         static assert(!__traits(compiles, cdt.roll!"seconds"(4)));
2119         static assert(!__traits(compiles, idt.roll!"seconds"(4)));
2120     }
2121 
2122 
2123     import core.time : Duration;
2124     /++
2125         Gives the result of adding or subtracting a $(REF Duration, core,time)
2126         from this $(LREF DateTime).
2127 
2128         The legal types of arithmetic for $(LREF DateTime) using this operator
2129         are
2130 
2131         $(BOOKTABLE,
2132         $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
2133         $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
2134         )
2135 
2136         Params:
2137             duration = The $(REF Duration, core,time) to add to or subtract from
2138                        this $(LREF DateTime).
2139       +/
2140     DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
2141         if (op == "+" || op == "-")
2142     {
2143         DateTime retval = this;
2144         immutable seconds = duration.total!"seconds";
2145         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
2146     }
2147 
2148     ///
2149     @safe unittest
2150     {
2151         import core.time : hours, seconds;
2152 
2153         assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
2154                DateTime(2016, 1, 1, 0, 0, 0));
2155 
2156         assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
2157                DateTime(2016, 1, 1, 0, 59, 59));
2158 
2159         assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
2160                DateTime(2015, 12, 31, 23, 59, 59));
2161 
2162         assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
2163                DateTime(2015, 12, 31, 23, 59, 59));
2164     }
2165 
2166     @safe unittest
2167     {
2168         import core.time : dur;
2169 
2170         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2171 
2172         assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2173         assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2174         assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2175         assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2176 
2177         assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2178         assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2179         assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2180         assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2181         assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2182         assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2183         assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2184         assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2185         assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2186         assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2187         assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2188         assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2189 
2190         assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2191         assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2192         assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2193         assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2194 
2195         assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2196         assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2197         assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2198         assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2199         assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2200         assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2201         assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2202         assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2203         assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2204         assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2205         assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2206         assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2207 
2208         auto duration = dur!"seconds"(12);
2209         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2210         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2211         assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2212         assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2213         assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2214         assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2215     }
2216 
2217 
2218     /++
2219         Gives the result of adding or subtracting a duration from this
2220         $(LREF DateTime), as well as assigning the result to this
2221         $(LREF DateTime).
2222 
2223         The legal types of arithmetic for $(LREF DateTime) using this operator
2224         are
2225 
2226         $(BOOKTABLE,
2227         $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime))
2228         $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime))
2229         )
2230 
2231         Params:
2232             duration = The duration to add to or subtract from this
2233                        $(LREF DateTime).
2234       +/
2235     ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
2236         if (op == "+" || op == "-")
2237     {
2238         import core.time : convert;
2239         import std.format : format;
2240 
2241         DateTime retval = this;
2242         immutable hnsecs = duration.total!"hnsecs";
2243 
2244         mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
2245     }
2246 
2247     @safe unittest
2248     {
2249         import core.time : dur;
2250         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
2251                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2252         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
2253                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2254         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) ==
2255                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2256         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) ==
2257                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2258 
2259         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) ==
2260                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2261         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) ==
2262                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2263         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) ==
2264                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2265         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) ==
2266                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2267         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) ==
2268                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2269         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) ==
2270                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2271         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) ==
2272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2273         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) ==
2274                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2275         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) ==
2276                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2277         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) ==
2278                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2279         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) ==
2280                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2281         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) ==
2282                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2283 
2284         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) ==
2285                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2286         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) ==
2287                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) ==
2289                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) ==
2291                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2292 
2293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) ==
2294                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) ==
2296                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) ==
2298                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) ==
2300                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) ==
2302                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) ==
2304                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2305         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) ==
2306                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2307         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) ==
2308                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2309         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) ==
2310                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2311         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) ==
2312                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2313         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) ==
2314                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) ==
2316                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2317 
2318         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2319         (dt += dur!"seconds"(92)) -= dur!"days"(-500);
2320         assert(dt == DateTime(2001, 6, 14, 9, 8, 38));
2321 
2322         auto duration = dur!"seconds"(12);
2323         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2324         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2325         static assert(!__traits(compiles, cdt += duration));
2326         static assert(!__traits(compiles, idt += duration));
2327         static assert(!__traits(compiles, cdt -= duration));
2328         static assert(!__traits(compiles, idt -= duration));
2329     }
2330 
2331 
2332     /++
2333         Gives the difference between two $(LREF DateTime)s.
2334 
2335         The legal types of arithmetic for $(LREF DateTime) using this operator are
2336 
2337         $(BOOKTABLE,
2338         $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
2339         )
2340       +/
2341     Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
2342         if (op == "-")
2343     {
2344         immutable dateResult = _date - rhs.date;
2345         immutable todResult = _tod - rhs._tod;
2346 
2347         import core.time : dur;
2348         return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
2349     }
2350 
2351     @safe unittest
2352     {
2353         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
2354 
2355         import core.time : dur;
2356         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
2357                dur!"seconds"(31_536_000));
2358         assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2359                dur!"seconds"(-31_536_000));
2360 
2361         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2362                dur!"seconds"(26_78_400));
2363         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) ==
2364                dur!"seconds"(-26_78_400));
2365 
2366         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) ==
2367                dur!"seconds"(86_400));
2368         assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2369                dur!"seconds"(-86_400));
2370 
2371         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) ==
2372                dur!"seconds"(3600));
2373         assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2374                dur!"seconds"(-3600));
2375 
2376         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2377                dur!"seconds"(60));
2378         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) ==
2379                dur!"seconds"(-60));
2380 
2381         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2382                dur!"seconds"(1));
2383         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) ==
2384                dur!"seconds"(-1));
2385 
2386         assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033));
2387         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033));
2388         assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367));
2389         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367));
2390 
2391         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2392         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2393         assert(dt - dt == Duration.zero);
2394         assert(cdt - dt == Duration.zero);
2395         assert(idt - dt == Duration.zero);
2396 
2397         assert(dt - cdt == Duration.zero);
2398         assert(cdt - cdt == Duration.zero);
2399         assert(idt - cdt == Duration.zero);
2400 
2401         assert(dt - idt == Duration.zero);
2402         assert(cdt - idt == Duration.zero);
2403         assert(idt - idt == Duration.zero);
2404     }
2405 
2406 
2407     /++
2408         Returns the difference between the two $(LREF DateTime)s in months.
2409 
2410         To get the difference in years, subtract the year property
2411         of two $(LREF DateTime)s. To get the difference in days or weeks,
2412         subtract the $(LREF DateTime)s themselves and use the
2413         $(REF Duration, core,time) that results. Because converting between
2414         months and smaller units requires a specific date (which
2415         $(REF Duration, core,time)s don't have), getting the difference in
2416         months requires some math using both the year and month properties, so
2417         this is a convenience function for getting the difference in months.
2418 
2419         Note that the number of days in the months or how far into the month
2420         either date is is irrelevant. It is the difference in the month property
2421         combined with the difference in years * 12. So, for instance,
2422         December 31st and January 1st are one month apart just as December 1st
2423         and January 31st are one month apart.
2424 
2425         Params:
2426             rhs = The $(LREF DateTime) to subtract from this one.
2427       +/
2428     int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
2429     {
2430         return _date.diffMonths(rhs._date);
2431     }
2432 
2433     ///
2434     @safe unittest
2435     {
2436         assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
2437                    DateTime(1999, 1, 31, 23, 59, 59)) == 1);
2438 
2439         assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
2440                    DateTime(1999, 2, 1, 12, 3, 42)) == -1);
2441 
2442         assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
2443                    DateTime(1999, 1, 1, 2, 4, 7)) == 2);
2444 
2445         assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
2446                    DateTime(1999, 3, 31, 0, 30, 58)) == -2);
2447     }
2448 
2449     @safe unittest
2450     {
2451         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2452         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2453         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2454         assert(dt.diffMonths(dt) == 0);
2455         assert(cdt.diffMonths(dt) == 0);
2456         assert(idt.diffMonths(dt) == 0);
2457 
2458         assert(dt.diffMonths(cdt) == 0);
2459         assert(cdt.diffMonths(cdt) == 0);
2460         assert(idt.diffMonths(cdt) == 0);
2461 
2462         assert(dt.diffMonths(idt) == 0);
2463         assert(cdt.diffMonths(idt) == 0);
2464         assert(idt.diffMonths(idt) == 0);
2465     }
2466 
2467 
2468     /++
2469         Whether this $(LREF DateTime) is in a leap year.
2470      +/
2471     @property bool isLeapYear() const @safe pure nothrow @nogc
2472     {
2473         return _date.isLeapYear;
2474     }
2475 
2476     @safe unittest
2477     {
2478         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2479         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2480         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2481         assert(!dt.isLeapYear);
2482         assert(!cdt.isLeapYear);
2483         assert(!idt.isLeapYear);
2484     }
2485 
2486 
2487     /++
2488         Day of the week this $(LREF DateTime) is on.
2489       +/
2490     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
2491     {
2492         return _date.dayOfWeek;
2493     }
2494 
2495     @safe unittest
2496     {
2497         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2498         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2499         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2500         assert(dt.dayOfWeek == DayOfWeek.tue);
2501         assert(cdt.dayOfWeek == DayOfWeek.tue);
2502         assert(idt.dayOfWeek == DayOfWeek.tue);
2503     }
2504 
2505 
2506     /++
2507         Day of the year this $(LREF DateTime) is on.
2508       +/
2509     @property ushort dayOfYear() const @safe pure nothrow @nogc
2510     {
2511         return _date.dayOfYear;
2512     }
2513 
2514     ///
2515     @safe unittest
2516     {
2517         assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
2518         assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
2519         assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
2520     }
2521 
2522     @safe unittest
2523     {
2524         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2525         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2526         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2527         assert(dt.dayOfYear == 187);
2528         assert(cdt.dayOfYear == 187);
2529         assert(idt.dayOfYear == 187);
2530     }
2531 
2532 
2533     /++
2534         Day of the year.
2535 
2536         Params:
2537             day = The day of the year to set which day of the year this
2538                   $(LREF DateTime) is on.
2539       +/
2540     @property void dayOfYear(int day) @safe pure
2541     {
2542         _date.dayOfYear = day;
2543     }
2544 
2545     @safe unittest
2546     {
2547         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2548         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2549         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2550         dt.dayOfYear = 12;
2551         assert(dt.dayOfYear == 12);
2552         static assert(!__traits(compiles, cdt.dayOfYear = 12));
2553         static assert(!__traits(compiles, idt.dayOfYear = 12));
2554     }
2555 
2556 
2557     /++
2558         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2559      +/
2560     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
2561     {
2562         return _date.dayOfGregorianCal;
2563     }
2564 
2565     ///
2566     @safe unittest
2567     {
2568         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
2569         assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
2570         assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
2571 
2572         assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
2573         assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
2574         assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
2575 
2576         assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
2577         assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
2578     }
2579 
2580     @safe unittest
2581     {
2582         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2583         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2584         assert(cdt.dayOfGregorianCal == 729_941);
2585         assert(idt.dayOfGregorianCal == 729_941);
2586     }
2587 
2588 
2589     /++
2590         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2591         Setting this property does not affect the time portion of
2592         $(LREF DateTime).
2593 
2594         Params:
2595             days = The day of the Gregorian Calendar to set this $(LREF DateTime)
2596                    to.
2597      +/
2598     @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
2599     {
2600         _date.dayOfGregorianCal = days;
2601     }
2602 
2603     ///
2604     @safe unittest
2605     {
2606         auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
2607         dt.dayOfGregorianCal = 1;
2608         assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
2609 
2610         dt.dayOfGregorianCal = 365;
2611         assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
2612 
2613         dt.dayOfGregorianCal = 366;
2614         assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
2615 
2616         dt.dayOfGregorianCal = 0;
2617         assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
2618 
2619         dt.dayOfGregorianCal = -365;
2620         assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
2621 
2622         dt.dayOfGregorianCal = -366;
2623         assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
2624 
2625         dt.dayOfGregorianCal = 730_120;
2626         assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
2627 
2628         dt.dayOfGregorianCal = 734_137;
2629         assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
2630     }
2631 
2632     @safe unittest
2633     {
2634         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2635         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2636         static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7));
2637         static assert(!__traits(compiles, idt.dayOfGregorianCal = 7));
2638     }
2639 
2640 
2641     /++
2642         The ISO 8601 week of the year that this $(LREF DateTime) is in.
2643 
2644         See_Also:
2645             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2646       +/
2647     @property ubyte isoWeek() const @safe pure nothrow
2648     {
2649         return _date.isoWeek;
2650     }
2651 
2652     @safe unittest
2653     {
2654         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2655         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2656         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2657         assert(dt.isoWeek == 27);
2658         assert(cdt.isoWeek == 27);
2659         assert(idt.isoWeek == 27);
2660     }
2661 
2662 
2663     /++
2664         The year of the ISO 8601 week calendar that this $(LREF DateTime) is in.
2665 
2666         See_Also:
2667             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2668       +/
2669     @property short isoWeekYear() const @safe pure nothrow
2670     {
2671         return _date.isoWeekYear;
2672     }
2673 
2674 
2675     /++
2676         $(LREF DateTime) for the last day in the month that this
2677         $(LREF DateTime) is in. The time portion of endOfMonth is always
2678         23:59:59.
2679       +/
2680     @property DateTime endOfMonth() const @safe pure nothrow
2681     {
2682         try
2683             return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
2684         catch (Exception e)
2685             assert(0, "DateTime constructor threw.");
2686     }
2687 
2688     ///
2689     @safe unittest
2690     {
2691         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
2692                DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
2693 
2694         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
2695                DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
2696 
2697         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
2698                DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
2699 
2700         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
2701                DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
2702     }
2703 
2704     @safe unittest
2705     {
2706         // Test A.D.
2707         assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59));
2708         assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59));
2709         assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59));
2710         assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59));
2711         assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59));
2712         assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59));
2713         assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59));
2714         assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2715         assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59));
2716         assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59));
2717         assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59));
2718         assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59));
2719         assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59));
2720 
2721         // Test B.C.
2722         assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59));
2723         assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59));
2724         assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59));
2725         assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59));
2726         assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59));
2727         assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59));
2728         assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59));
2729         assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59));
2730         assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59));
2731         assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59));
2732         assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59));
2733         assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59));
2734         assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59));
2735 
2736         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2737         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2738         assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2739         assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2740     }
2741 
2742 
2743     /++
2744         The last day in the month that this $(LREF DateTime) is in.
2745       +/
2746     @property ubyte daysInMonth() const @safe pure nothrow @nogc
2747     {
2748         return _date.daysInMonth;
2749     }
2750 
2751     ///
2752     @safe unittest
2753     {
2754         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
2755         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
2756         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
2757         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
2758     }
2759 
2760     @safe unittest
2761     {
2762         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2763         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2764         assert(cdt.daysInMonth == 31);
2765         assert(idt.daysInMonth == 31);
2766     }
2767 
2768 
2769     /++
2770         Whether the current year is a date in A.D.
2771       +/
2772     @property bool isAD() const @safe pure nothrow @nogc
2773     {
2774         return _date.isAD;
2775     }
2776 
2777     ///
2778     @safe unittest
2779     {
2780         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
2781         assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
2782         assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
2783         assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
2784     }
2785 
2786     @safe unittest
2787     {
2788         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2789         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2790         assert(cdt.isAD);
2791         assert(idt.isAD);
2792     }
2793 
2794 
2795     /++
2796         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
2797         $(LREF DateTime) at the given time. For example, prior to noon,
2798         1996-03-31 would be the Julian day number 2_450_173, so this function
2799         returns 2_450_173, while from noon onward, the julian day number would
2800         be 2_450_174, so this function returns 2_450_174.
2801       +/
2802     @property long julianDay() const @safe pure nothrow @nogc
2803     {
2804         if (_tod._hour < 12)
2805             return _date.julianDay - 1;
2806         else
2807             return _date.julianDay;
2808     }
2809 
2810     @safe unittest
2811     {
2812         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1);
2813         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0);
2814 
2815         assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424);
2816         assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425);
2817 
2818         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425);
2819         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426);
2820 
2821         assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160);
2822         assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161);
2823 
2824         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000);
2825         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001);
2826 
2827         assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973);
2828         assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974);
2829 
2830         assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173);
2831         assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174);
2832 
2833         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432);
2834         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433);
2835 
2836         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2837         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2838         assert(cdt.julianDay == 2_451_366);
2839         assert(idt.julianDay == 2_451_366);
2840     }
2841 
2842 
2843     /++
2844         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
2845         time on this date (since, the modified Julian day changes at midnight).
2846       +/
2847     @property long modJulianDay() const @safe pure nothrow @nogc
2848     {
2849         return _date.modJulianDay;
2850     }
2851 
2852     @safe unittest
2853     {
2854         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0);
2855         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0);
2856 
2857         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432);
2858         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432);
2859 
2860         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2861         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2862         assert(cdt.modJulianDay == 51_365);
2863         assert(idt.modJulianDay == 51_365);
2864     }
2865 
2866 
2867     /++
2868         Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`.
2869         If `writer` is set, the resulting string will be written directly to it.
2870 
2871         Params:
2872             writer = A `char` accepting
2873             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2874         Returns:
2875             A `string` when not using an output range; `void` otherwise.
2876       +/
2877     string toISOString() const @safe pure nothrow
2878     {
2879         import std.array : appender;
2880         auto w = appender!string();
2881         w.reserve(18);
2882         try
2883             toISOString(w);
2884         catch (Exception e)
2885             assert(0, "toISOString() threw.");
2886         return w.data;
2887     }
2888 
2889     /// ditto
2890     void toISOString(W)(ref W writer) const
2891     if (isOutputRange!(W, char))
2892     {
2893         import std.format.write : formattedWrite;
2894         _date.toISOString(writer);
2895         formattedWrite!("T%02d%02d%02d")(
2896             writer,
2897             _tod._hour,
2898             _tod._minute,
2899             _tod._second
2900         );
2901     }
2902 
2903     ///
2904     @safe unittest
2905     {
2906         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
2907                "20100704T070612");
2908 
2909         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
2910                "19981225T021500");
2911 
2912         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
2913                "00000105T230959");
2914 
2915         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
2916                "-00040105T000002");
2917     }
2918 
2919     @safe unittest
2920     {
2921         // Test A.D.
2922         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000");
2923         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612");
2924         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459");
2925         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959");
2926         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101");
2927 
2928         // Test B.C.
2929         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204");
2930         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000");
2931         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612");
2932         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459");
2933         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959");
2934         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101");
2935 
2936         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2937         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2938         assert(cdt.toISOString() == "19990706T123033");
2939         assert(idt.toISOString() == "19990706T123033");
2940     }
2941 
2942 
2943     /++
2944         Converts this $(LREF DateTime) to a string with the format
2945         `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting
2946         string will be written directly to it.
2947 
2948         Params:
2949             writer = A `char` accepting
2950             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2951         Returns:
2952             A `string` when not using an output range; `void` otherwise.
2953       +/
2954     string toISOExtString() const @safe pure nothrow
2955     {
2956         import std.array : appender;
2957         auto w = appender!string();
2958         w.reserve(20);
2959         try
2960             toISOExtString(w);
2961         catch (Exception e)
2962             assert(0, "toISOExtString() threw.");
2963         return w.data;
2964     }
2965 
2966     /// ditto
2967     void toISOExtString(W)(ref W writer) const
2968     if (isOutputRange!(W, char))
2969     {
2970         import std.format.write : formattedWrite;
2971         _date.toISOExtString(writer);
2972         formattedWrite!("T%02d:%02d:%02d")(
2973             writer,
2974             _tod._hour,
2975             _tod._minute,
2976             _tod._second
2977         );
2978     }
2979 
2980     ///
2981     @safe unittest
2982     {
2983         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
2984                "2010-07-04T07:06:12");
2985 
2986         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
2987                "1998-12-25T02:15:00");
2988 
2989         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
2990                "0000-01-05T23:09:59");
2991 
2992         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
2993                "-0004-01-05T00:00:02");
2994     }
2995 
2996     @safe unittest
2997     {
2998         // Test A.D.
2999         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00");
3000         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12");
3001         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59");
3002         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59");
3003         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01");
3004 
3005         // Test B.C.
3006         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04");
3007         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00");
3008         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12");
3009         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59");
3010         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59");
3011         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01");
3012 
3013         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3014         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3015         assert(cdt.toISOExtString() == "1999-07-06T12:30:33");
3016         assert(idt.toISOExtString() == "1999-07-06T12:30:33");
3017     }
3018 
3019     /++
3020         Converts this $(LREF DateTime) to a string with the format
3021         `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting
3022         string will be written directly to it.
3023 
3024         Params:
3025             writer = A `char` accepting
3026             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3027         Returns:
3028             A `string` when not using an output range; `void` otherwise.
3029       +/
3030     string toSimpleString() const @safe pure nothrow
3031     {
3032         import std.array : appender;
3033         auto w = appender!string();
3034         w.reserve(22);
3035         try
3036             toSimpleString(w);
3037         catch (Exception e)
3038             assert(0, "toSimpleString() threw.");
3039         return w.data;
3040     }
3041 
3042     /// ditto
3043     void toSimpleString(W)(ref W writer) const
3044     if (isOutputRange!(W, char))
3045     {
3046         import std.format.write : formattedWrite;
3047         _date.toSimpleString(writer);
3048         formattedWrite!(" %02d:%02d:%02d")(
3049             writer,
3050             _tod._hour,
3051             _tod._minute,
3052             _tod._second
3053         );
3054     }
3055 
3056     ///
3057     @safe unittest
3058     {
3059         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
3060                "2010-Jul-04 07:06:12");
3061 
3062         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
3063                "1998-Dec-25 02:15:00");
3064 
3065         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
3066                "0000-Jan-05 23:09:59");
3067 
3068         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
3069                "-0004-Jan-05 00:00:02");
3070     }
3071 
3072     @safe unittest
3073     {
3074         // Test A.D.
3075         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00");
3076         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12");
3077         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59");
3078         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59");
3079         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01");
3080 
3081         // Test B.C.
3082         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04");
3083         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00");
3084         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12");
3085         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59");
3086         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59");
3087         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01");
3088 
3089         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3090         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3091         assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33");
3092         assert(idt.toSimpleString() == "1999-Jul-06 12:30:33");
3093     }
3094 
3095 
3096     /++
3097         Converts this $(LREF DateTime) to a string.
3098 
3099         This function exists to make it easy to convert a $(LREF DateTime) to a
3100         string for code that does not care what the exact format is - just that
3101         it presents the information in a clear manner. It also makes it easy to
3102         simply convert a $(LREF DateTime) to a string when using functions such
3103         as `to!string`, `format`, or `writeln` which use toString to convert
3104         user-defined types. So, it is unlikely that much code will call
3105         toString directly.
3106 
3107         The format of the string is purposefully unspecified, and code that
3108         cares about the format of the string should use `toISOString`,
3109         `toISOExtString`, `toSimpleString`, or some other custom formatting
3110         function that explicitly generates the format that the code needs. The
3111         reason is that the code is then clear about what format it's using,
3112         making it less error-prone to maintain the code and interact with other
3113         software that consumes the generated strings. It's for this same reason
3114         that $(LREF DateTime) has no `fromString` function, whereas it does have
3115         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
3116 
3117         The format returned by toString may or may not change in the future.
3118       +/
3119     string toString() const @safe pure nothrow
3120     {
3121         return toSimpleString();
3122     }
3123 
3124     @safe unittest
3125     {
3126         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3127         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3128         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3129         assert(dt.toString());
3130         assert(cdt.toString());
3131         assert(idt.toString());
3132     }
3133 
3134     /// ditto
3135     void toString(W)(ref W writer) const
3136     if (isOutputRange!(W, char))
3137     {
3138         toSimpleString(writer);
3139     }
3140 
3141     /++
3142         Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
3143         Whitespace is stripped from the given string.
3144 
3145         Params:
3146             isoString = A string formatted in the ISO format for dates and times.
3147 
3148         Throws:
3149             $(REF DateTimeException,std,datetime,date) if the given string is
3150             not in the ISO format or if the resulting $(LREF DateTime) would not
3151             be valid.
3152       +/
3153     static DateTime fromISOString(S)(scope const S isoString) @safe pure
3154         if (isSomeString!S)
3155     {
3156         import std.algorithm.searching : countUntil;
3157         import std.exception : enforce;
3158         import std.format : format;
3159         import std.string : strip;
3160         import std.utf : byCodeUnit;
3161 
3162         auto str = strip(isoString);
3163 
3164         enforce!DateTimeException(str.length >= 15, format("Invalid format for DateTime.fromISOString %s", isoString));
3165         auto t = str.byCodeUnit.countUntil('T');
3166 
3167         enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromISOString: %s", isoString));
3168 
3169         immutable date = Date.fromISOString(str[0 .. t]);
3170         immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
3171 
3172         return DateTime(date, tod);
3173     }
3174 
3175     ///
3176     @safe unittest
3177     {
3178         assert(DateTime.fromISOString("20100704T070612") ==
3179                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3180 
3181         assert(DateTime.fromISOString("19981225T021500") ==
3182                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3183 
3184         assert(DateTime.fromISOString("00000105T230959") ==
3185                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3186 
3187         assert(DateTime.fromISOString("-00040105T000002") ==
3188                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3189 
3190         assert(DateTime.fromISOString(" 20100704T070612 ") ==
3191                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3192     }
3193 
3194     @safe unittest
3195     {
3196         assertThrown!DateTimeException(DateTime.fromISOString(""));
3197         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3198         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3199         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3200         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3201         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3202 
3203         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3204         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3205         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3206         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3207         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3208 
3209         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3210         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3211         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3212         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3213         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3214         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3215 
3216         assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
3217         assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
3218 
3219         assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3220         assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3221         assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3222         assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3223         assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3224         assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3225         assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3226     }
3227 
3228     // https://issues.dlang.org/show_bug.cgi?id=17801
3229     @safe unittest
3230     {
3231         import std.conv : to;
3232         import std.meta : AliasSeq;
3233         static foreach (C; AliasSeq!(char, wchar, dchar))
3234         {
3235             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3236                 assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
3237         }
3238     }
3239 
3240 
3241     /++
3242         Creates a $(LREF DateTime) from a string with the format
3243         YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string.
3244 
3245         Params:
3246             isoExtString = A string formatted in the ISO Extended format for dates
3247                            and times.
3248 
3249         Throws:
3250             $(REF DateTimeException,std,datetime,date) if the given string is
3251             not in the ISO Extended format or if the resulting $(LREF DateTime)
3252             would not be valid.
3253       +/
3254     static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
3255         if (isSomeString!(S))
3256     {
3257         import std.algorithm.searching : countUntil;
3258         import std.exception : enforce;
3259         import std.format : format;
3260         import std.string : strip;
3261         import std.utf : byCodeUnit;
3262 
3263         auto str = strip(isoExtString);
3264 
3265         enforce!DateTimeException(str.length >= 15,
3266                                   format("Invalid format for DateTime.fromISOExtString: %s", isoExtString));
3267         auto t = str.byCodeUnit.countUntil('T');
3268 
3269         enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromISOExtString: %s", isoExtString));
3270 
3271         immutable date = Date.fromISOExtString(str[0 .. t]);
3272         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3273 
3274         return DateTime(date, tod);
3275     }
3276 
3277     ///
3278     @safe unittest
3279     {
3280         assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
3281                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3282 
3283         assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
3284                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3285 
3286         assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
3287                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3288 
3289         assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
3290                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3291 
3292         assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
3293                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3294     }
3295 
3296     @safe unittest
3297     {
3298         assertThrown!DateTimeException(DateTime.fromISOExtString(""));
3299         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
3300         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
3301         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
3302         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
3303         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
3304 
3305         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
3306         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3307         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3308         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
3309         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
3310         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
3311 
3312         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
3313         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
3314         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
3315         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
3316 
3317         assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
3318         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
3319 
3320         assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3321         assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3322         assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3323         assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3324         assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3325         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3326         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3327     }
3328 
3329     // https://issues.dlang.org/show_bug.cgi?id=17801
3330     @safe unittest
3331     {
3332         import std.conv : to;
3333         import std.meta : AliasSeq;
3334         static foreach (C; AliasSeq!(char, wchar, dchar))
3335         {
3336             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3337                 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3338         }
3339     }
3340 
3341 
3342     /++
3343         Creates a $(LREF DateTime) from a string with the format
3344         YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
3345 
3346         Params:
3347             simpleString = A string formatted in the way that toSimpleString
3348                            formats dates and times.
3349 
3350         Throws:
3351             $(REF DateTimeException,std,datetime,date) if the given string is
3352             not in the correct format or if the resulting $(LREF DateTime)
3353             would not be valid.
3354       +/
3355     static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
3356         if (isSomeString!(S))
3357     {
3358         import std.algorithm.searching : countUntil;
3359         import std.exception : enforce;
3360         import std.format : format;
3361         import std.string : strip;
3362         import std.utf : byCodeUnit;
3363 
3364         auto str = strip(simpleString);
3365 
3366         enforce!DateTimeException(str.length >= 15,
3367                                   format("Invalid format for DateTime.fromSimpleString: %s", simpleString));
3368         auto t = str.byCodeUnit.countUntil(' ');
3369 
3370         enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromSimpleString: %s", simpleString));
3371 
3372         immutable date = Date.fromSimpleString(str[0 .. t]);
3373         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3374 
3375         return DateTime(date, tod);
3376     }
3377 
3378     ///
3379     @safe unittest
3380     {
3381         assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
3382                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3383         assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
3384                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3385         assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
3386                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3387         assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
3388                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3389         assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
3390                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3391     }
3392 
3393     @safe unittest
3394     {
3395         assertThrown!DateTimeException(DateTime.fromISOString(""));
3396         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3397         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3398         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3399         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3400         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3401 
3402         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3403         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3404         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3405         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3406         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3407 
3408         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3409         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3410         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3411         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3412         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3413         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3414 
3415         assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
3416         assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
3417 
3418         assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
3419                DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3420         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
3421                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3422         assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
3423                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3424         assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
3425                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3426         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
3427                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3428         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
3429                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3430         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
3431                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3432     }
3433 
3434     // https://issues.dlang.org/show_bug.cgi?id=17801
3435     @safe unittest
3436     {
3437         import std.conv : to;
3438         import std.meta : AliasSeq;
3439         static foreach (C; AliasSeq!(char, wchar, dchar))
3440         {
3441             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3442                 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3443         }
3444     }
3445 
3446 
3447     /++
3448         Returns the $(LREF DateTime) farthest in the past which is representable
3449         by $(LREF DateTime).
3450       +/
3451     @property static DateTime min() @safe pure nothrow @nogc
3452     out(result)
3453     {
3454         assert(result._date == Date.min);
3455         assert(result._tod == TimeOfDay.min);
3456     }
3457     do
3458     {
3459         auto dt = DateTime.init;
3460         dt._date._year = short.min;
3461         dt._date._month = Month.jan;
3462         dt._date._day = 1;
3463 
3464         return dt;
3465     }
3466 
3467     @safe unittest
3468     {
3469         assert(DateTime.min.year < 0);
3470         assert(DateTime.min < DateTime.max);
3471     }
3472 
3473 
3474     /++
3475         Returns the $(LREF DateTime) farthest in the future which is
3476         representable by $(LREF DateTime).
3477       +/
3478     @property static DateTime max() @safe pure nothrow @nogc
3479     out(result)
3480     {
3481         assert(result._date == Date.max);
3482         assert(result._tod == TimeOfDay.max);
3483     }
3484     do
3485     {
3486         auto dt = DateTime.init;
3487         dt._date._year = short.max;
3488         dt._date._month = Month.dec;
3489         dt._date._day = 31;
3490         dt._tod._hour = TimeOfDay.maxHour;
3491         dt._tod._minute = TimeOfDay.maxMinute;
3492         dt._tod._second = TimeOfDay.maxSecond;
3493 
3494         return dt;
3495     }
3496 
3497     @safe unittest
3498     {
3499         assert(DateTime.max.year > 0);
3500         assert(DateTime.max > DateTime.min);
3501     }
3502 
3503 
3504 private:
3505 
3506     /+
3507         Add seconds to the time of day. Negative values will subtract. If the
3508         number of seconds overflows (or underflows), then the seconds will wrap,
3509         increasing (or decreasing) the number of minutes accordingly. The
3510         same goes for any larger units.
3511 
3512         Params:
3513             seconds = The number of seconds to add to this $(LREF DateTime).
3514       +/
3515     ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
3516     {
3517         import core.time : convert;
3518         long hnsecs = convert!("seconds", "hnsecs")(seconds);
3519         hnsecs += convert!("hours", "hnsecs")(_tod._hour);
3520         hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
3521         hnsecs += convert!("seconds", "hnsecs")(_tod._second);
3522 
3523         auto days = splitUnitsFromHNSecs!"days"(hnsecs);
3524 
3525         if (hnsecs < 0)
3526         {
3527             hnsecs += convert!("days", "hnsecs")(1);
3528             --days;
3529         }
3530 
3531         _date._addDays(days);
3532 
3533         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
3534         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
3535         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
3536 
3537         _tod._hour = cast(ubyte) newHours;
3538         _tod._minute = cast(ubyte) newMinutes;
3539         _tod._second = cast(ubyte) newSeconds;
3540 
3541         return this;
3542     }
3543 
3544     @safe unittest
3545     {
3546         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
3547         {
3548             orig._addSeconds(seconds);
3549             assert(orig == expected);
3550         }
3551 
3552         // Test A.D.
3553         testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
3554         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
3555         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
3556         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
3557         testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
3558         testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
3559         testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
3560         testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
3561         testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
3562         testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
3563         testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
3564         testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
3565         testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
3566         testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
3567 
3568         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
3569         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
3570         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
3571         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
3572         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
3573         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
3574         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
3575         testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
3576         testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
3577 
3578         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
3579         testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
3580         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
3581         testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
3582         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
3583         testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
3584         testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
3585         testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
3586         testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
3587         testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
3588         testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
3589         testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
3590         testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
3591 
3592         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
3593         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
3594         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
3595         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
3596         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
3597         testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
3598 
3599         testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
3600         testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
3601         testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
3602 
3603         testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
3604         testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
3605         testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
3606 
3607         testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
3608         testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
3609         testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
3610 
3611         testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
3612         testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
3613         testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
3614 
3615         testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
3616         testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
3617         testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
3618 
3619         testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
3620         testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
3621         testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
3622 
3623         // Test B.C.
3624         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
3625         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
3626         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
3627         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
3628         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
3629         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
3630         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
3631         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
3632         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
3633         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
3634         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
3635         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
3636         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
3637         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
3638 
3639         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
3640         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
3641         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
3642         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
3643         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
3644         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
3645         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
3646         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
3647         testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
3648 
3649         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
3650         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
3651         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
3652         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
3653         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
3654         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
3655         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
3656         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
3657         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
3658         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
3659         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
3660         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
3661         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
3662 
3663         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
3664         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
3665         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
3666         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
3667         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
3668         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
3669         testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
3670 
3671         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
3672         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
3673         testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
3674 
3675         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
3676         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
3677         testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
3678 
3679         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
3680         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
3681         testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
3682 
3683         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
3684         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
3685         testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
3686 
3687         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
3688         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
3689         testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
3690 
3691         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
3692         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
3693         testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
3694 
3695         // Test Both
3696         testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
3697         testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
3698 
3699         testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
3700         testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
3701 
3702         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
3703         testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
3704 
3705         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
3706         testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
3707 
3708         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3709         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3710         static assert(!__traits(compiles, cdt._addSeconds(4)));
3711         static assert(!__traits(compiles, idt._addSeconds(4)));
3712     }
3713 
3714 
3715     Date      _date;
3716     TimeOfDay _tod;
3717 }
3718 
3719 ///
3720 @safe pure unittest
3721 {
3722     import core.time : days, seconds;
3723 
3724     auto dt = DateTime(2000, 6, 1, 10, 30, 0);
3725 
3726     assert(dt.date == Date(2000, 6, 1));
3727     assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
3728     assert(dt.dayOfYear == 153);
3729     assert(dt.dayOfWeek == DayOfWeek.thu);
3730 
3731     dt += 10.days + 100.seconds;
3732     assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
3733 
3734     assert(dt.toISOExtString() == "2000-06-11T10:31:40");
3735     assert(dt.toISOString() == "20000611T103140");
3736     assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
3737 
3738     assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3739     assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
3740     assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3741 }
3742 
3743 /++
3744     Represents a date in the
3745     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
3746     Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
3747     are A.D. Non-positive years are B.C.
3748 
3749     Year, month, and day are kept separately internally so that `Date` is
3750     optimized for calendar-based operations.
3751 
3752     `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
3753     leap year calculations for its entire length. As per
3754     $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
3755     year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
3756     as a positive integer with 1 B.C. being the year prior to 1 A.D.
3757 
3758     Year 0 is a leap year.
3759  +/
3760 struct Date
3761 {
3762 public:
3763 
3764     /++
3765         Throws:
3766             $(REF DateTimeException,std,datetime,date) if the resulting
3767             $(LREF Date) would not be valid.
3768 
3769         Params:
3770             year  = Year of the Gregorian Calendar. Positive values are A.D.
3771                     Non-positive values are B.C. with year 0 being the year
3772                     prior to 1 A.D.
3773             month = Month of the year (January is 1).
3774             day   = Day of the month.
3775      +/
3776     this(int year, int month, int day) @safe pure
3777     {
3778         enforceValid!"months"(cast(Month) month);
3779         enforceValid!"days"(year, cast(Month) month, day);
3780 
3781         _year  = year.castToYear;
3782         _month = cast(Month) month;
3783         _day   = cast(ubyte) day;
3784     }
3785 
3786     @safe unittest
3787     {
3788         import std.exception : assertNotThrown;
3789         assert(Date(1, 1, 1) == Date.init);
3790 
3791         static void testDate(Date date, int year, int month, int day)
3792         {
3793             assert(date._year == year);
3794             assert(date._month == month);
3795             assert(date._day == day);
3796         }
3797 
3798         testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
3799         testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
3800         testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
3801 
3802         // Test A.D.
3803         assertThrown!DateTimeException(Date(1, 0, 1));
3804         assertThrown!DateTimeException(Date(1, 1, 0));
3805         assertThrown!DateTimeException(Date(1999, 13, 1));
3806         assertThrown!DateTimeException(Date(1999, 1, 32));
3807         assertThrown!DateTimeException(Date(1999, 2, 29));
3808         assertThrown!DateTimeException(Date(2000, 2, 30));
3809         assertThrown!DateTimeException(Date(1999, 3, 32));
3810         assertThrown!DateTimeException(Date(1999, 4, 31));
3811         assertThrown!DateTimeException(Date(1999, 5, 32));
3812         assertThrown!DateTimeException(Date(1999, 6, 31));
3813         assertThrown!DateTimeException(Date(1999, 7, 32));
3814         assertThrown!DateTimeException(Date(1999, 8, 32));
3815         assertThrown!DateTimeException(Date(1999, 9, 31));
3816         assertThrown!DateTimeException(Date(1999, 10, 32));
3817         assertThrown!DateTimeException(Date(1999, 11, 31));
3818         assertThrown!DateTimeException(Date(1999, 12, 32));
3819         assertThrown!DateTimeException(Date(short.max+1, 1, 1));
3820 
3821         assertNotThrown!DateTimeException(Date(1999, 1, 31));
3822         assertNotThrown!DateTimeException(Date(1999, 2, 28));
3823         assertNotThrown!DateTimeException(Date(2000, 2, 29));
3824         assertNotThrown!DateTimeException(Date(1999, 3, 31));
3825         assertNotThrown!DateTimeException(Date(1999, 4, 30));
3826         assertNotThrown!DateTimeException(Date(1999, 5, 31));
3827         assertNotThrown!DateTimeException(Date(1999, 6, 30));
3828         assertNotThrown!DateTimeException(Date(1999, 7, 31));
3829         assertNotThrown!DateTimeException(Date(1999, 8, 31));
3830         assertNotThrown!DateTimeException(Date(1999, 9, 30));
3831         assertNotThrown!DateTimeException(Date(1999, 10, 31));
3832         assertNotThrown!DateTimeException(Date(1999, 11, 30));
3833         assertNotThrown!DateTimeException(Date(1999, 12, 31));
3834 
3835         // Test B.C.
3836         assertNotThrown!DateTimeException(Date(0, 1, 1));
3837         assertNotThrown!DateTimeException(Date(-1, 1, 1));
3838         assertNotThrown!DateTimeException(Date(-1, 12, 31));
3839         assertNotThrown!DateTimeException(Date(-1, 2, 28));
3840         assertNotThrown!DateTimeException(Date(-4, 2, 29));
3841 
3842         assertThrown!DateTimeException(Date(-1, 2, 29));
3843         assertThrown!DateTimeException(Date(-2, 2, 29));
3844         assertThrown!DateTimeException(Date(-3, 2, 29));
3845         assertThrown!DateTimeException(Date(short.min-1, 1, 1));
3846     }
3847 
3848 
3849     /++
3850         Params:
3851             day = The Xth day of the Gregorian Calendar that the constructed
3852                   $(LREF Date) will be for.
3853      +/
3854     this(int day) @safe pure nothrow @nogc
3855     {
3856         if (day > 0)
3857         {
3858             int years = (day / daysIn400Years) * 400 + 1;
3859             day %= daysIn400Years;
3860 
3861             {
3862                 immutable tempYears = day / daysIn100Years;
3863 
3864                 if (tempYears == 4)
3865                 {
3866                     years += 300;
3867                     day -= daysIn100Years * 3;
3868                 }
3869                 else
3870                 {
3871                     years += tempYears * 100;
3872                     day %= daysIn100Years;
3873                 }
3874             }
3875 
3876             years += (day / daysIn4Years) * 4;
3877             day %= daysIn4Years;
3878 
3879             {
3880                 immutable tempYears = day / daysInYear;
3881 
3882                 if (tempYears == 4)
3883                 {
3884                     years += 3;
3885                     day -= daysInYear * 3;
3886                 }
3887                 else
3888                 {
3889                     years += tempYears;
3890                     day %= daysInYear;
3891                 }
3892             }
3893 
3894             if (day == 0)
3895             {
3896                 _year = cast(short)(years - 1);
3897                 _month = Month.dec;
3898                 _day = 31;
3899             }
3900             else
3901             {
3902                 _year = cast(short) years;
3903 
3904                 setDayOfYear(day);
3905             }
3906         }
3907         else if (day <= 0 && -day < daysInLeapYear)
3908         {
3909             _year = 0;
3910 
3911             setDayOfYear(daysInLeapYear + day);
3912         }
3913         else
3914         {
3915             day += daysInLeapYear - 1;
3916             int years = (day / daysIn400Years) * 400 - 1;
3917             day %= daysIn400Years;
3918 
3919             {
3920                 immutable tempYears = day / daysIn100Years;
3921 
3922                 if (tempYears == -4)
3923                 {
3924                     years -= 300;
3925                     day += daysIn100Years * 3;
3926                 }
3927                 else
3928                 {
3929                     years += tempYears * 100;
3930                     day %= daysIn100Years;
3931                 }
3932             }
3933 
3934             years += (day / daysIn4Years) * 4;
3935             day %= daysIn4Years;
3936 
3937             {
3938                 immutable tempYears = day / daysInYear;
3939 
3940                 if (tempYears == -4)
3941                 {
3942                     years -= 3;
3943                     day += daysInYear * 3;
3944                 }
3945                 else
3946                 {
3947                     years += tempYears;
3948                     day %= daysInYear;
3949                 }
3950             }
3951 
3952             if (day == 0)
3953             {
3954                 _year = cast(short)(years + 1);
3955                 _month = Month.jan;
3956                 _day = 1;
3957             }
3958             else
3959             {
3960                 _year = cast(short) years;
3961                 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
3962 
3963                 setDayOfYear(newDoY);
3964             }
3965         }
3966     }
3967 
3968     @safe unittest
3969     {
3970         import std.range : chain;
3971 
3972         // Test A.D.
3973         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
3974             assert(Date(gd.day) == gd.date);
3975     }
3976 
3977 
3978     /++
3979         Compares this $(LREF Date) with the given $(LREF Date).
3980 
3981         Returns:
3982             $(BOOKTABLE,
3983             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
3984             $(TR $(TD this == rhs) $(TD 0))
3985             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
3986             )
3987      +/
3988     int opCmp(Date rhs) const @safe pure nothrow @nogc
3989     {
3990         if (_year < rhs._year)
3991             return -1;
3992         if (_year > rhs._year)
3993             return 1;
3994 
3995         if (_month < rhs._month)
3996             return -1;
3997         if (_month > rhs._month)
3998             return 1;
3999 
4000         if (_day < rhs._day)
4001             return -1;
4002         if (_day > rhs._day)
4003             return 1;
4004 
4005         return 0;
4006     }
4007 
4008     @safe unittest
4009     {
4010         // Test A.D.
4011         assert(Date(1, 1, 1).opCmp(Date.init) == 0);
4012 
4013         assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
4014         assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
4015         assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
4016 
4017         assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
4018         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
4019 
4020         assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
4021 
4022         assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
4023         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
4024         assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
4025         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
4026         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
4027         assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
4028 
4029         assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
4030         assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4031         assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
4032         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
4033         assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
4034         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4035 
4036         // Test B.C.
4037         assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
4038         assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
4039         assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
4040         assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
4041 
4042         assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
4043         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
4044 
4045         assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
4046 
4047         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
4048         assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
4049         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
4050         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
4051         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4052         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
4053 
4054         assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
4055         assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
4056         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4057         assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
4058         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
4059         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
4060 
4061         // Test Both
4062         assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
4063         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
4064 
4065         assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
4066         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
4067 
4068         assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
4069         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
4070 
4071         assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
4072         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
4073 
4074         assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
4075         assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
4076 
4077         auto date = Date(1999, 7, 6);
4078         const cdate = Date(1999, 7, 6);
4079         immutable idate = Date(1999, 7, 6);
4080         assert(date.opCmp(date) == 0);
4081         assert(date.opCmp(cdate) == 0);
4082         assert(date.opCmp(idate) == 0);
4083         assert(cdate.opCmp(date) == 0);
4084         assert(cdate.opCmp(cdate) == 0);
4085         assert(cdate.opCmp(idate) == 0);
4086         assert(idate.opCmp(date) == 0);
4087         assert(idate.opCmp(cdate) == 0);
4088         assert(idate.opCmp(idate) == 0);
4089     }
4090 
4091 
4092     /++
4093         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4094         are B.C.
4095      +/
4096     @property short year() const @safe pure nothrow @nogc
4097     {
4098         return _year;
4099     }
4100 
4101     ///
4102     @safe unittest
4103     {
4104         assert(Date(1999, 7, 6).year == 1999);
4105         assert(Date(2010, 10, 4).year == 2010);
4106         assert(Date(-7, 4, 5).year == -7);
4107     }
4108 
4109     @safe unittest
4110     {
4111         assert(Date.init.year == 1);
4112         assert(Date(1999, 7, 6).year == 1999);
4113         assert(Date(-1999, 7, 6).year == -1999);
4114 
4115         const cdate = Date(1999, 7, 6);
4116         immutable idate = Date(1999, 7, 6);
4117         assert(cdate.year == 1999);
4118         assert(idate.year == 1999);
4119     }
4120 
4121     /++
4122         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4123         are B.C.
4124 
4125         Params:
4126             year = The year to set this Date's year to.
4127 
4128         Throws:
4129             $(REF DateTimeException,std,datetime,date) if the new year is not
4130             a leap year and the resulting date would be on February 29th.
4131      +/
4132     @property void year(int year) @safe pure
4133     {
4134         enforceValid!"days"(year, _month, _day);
4135         _year = year.castToYear;
4136     }
4137 
4138     ///
4139     @safe unittest
4140     {
4141         assert(Date(1999, 7, 6).year == 1999);
4142         assert(Date(2010, 10, 4).year == 2010);
4143         assert(Date(-7, 4, 5).year == -7);
4144     }
4145 
4146     @safe unittest
4147     {
4148         static void testDateInvalid(Date date, int year)
4149         {
4150             date.year = year;
4151         }
4152 
4153         static void testDate(Date date, int year, Date expected)
4154         {
4155             date.year = year;
4156             assert(date == expected);
4157         }
4158 
4159         assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
4160 
4161         testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
4162         testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
4163         testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
4164 
4165         const cdate = Date(1999, 7, 6);
4166         immutable idate = Date(1999, 7, 6);
4167         static assert(!__traits(compiles, cdate.year = 1999));
4168         static assert(!__traits(compiles, idate.year = 1999));
4169     }
4170 
4171 
4172     /++
4173         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4174 
4175         Throws:
4176             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
4177      +/
4178     @property ushort yearBC() const @safe pure
4179     {
4180         import std.format : format;
4181 
4182         if (isAD)
4183             throw new DateTimeException(format("Year %s is A.D.", _year));
4184         return cast(ushort)((_year * -1) + 1);
4185     }
4186 
4187     ///
4188     @safe unittest
4189     {
4190         assert(Date(0, 1, 1).yearBC == 1);
4191         assert(Date(-1, 1, 1).yearBC == 2);
4192         assert(Date(-100, 1, 1).yearBC == 101);
4193     }
4194 
4195     @safe unittest
4196     {
4197         assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
4198 
4199         auto date = Date(0, 7, 6);
4200         const cdate = Date(0, 7, 6);
4201         immutable idate = Date(0, 7, 6);
4202         assert(date.yearBC == 1);
4203         assert(cdate.yearBC == 1);
4204         assert(idate.yearBC == 1);
4205     }
4206 
4207 
4208     /++
4209         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4210 
4211         Params:
4212             year = The year B.C. to set this $(LREF Date)'s year to.
4213 
4214         Throws:
4215             $(REF DateTimeException,std,datetime,date) if a non-positive value
4216             is given.
4217      +/
4218     @property void yearBC(int year) @safe pure
4219     {
4220         if (year <= 0)
4221             throw new DateTimeException("The given year is not a year B.C.");
4222         _year = castToYear((year - 1) * -1);
4223     }
4224 
4225     ///
4226     @safe unittest
4227     {
4228         auto date = Date(2010, 1, 1);
4229         date.yearBC = 1;
4230         assert(date == Date(0, 1, 1));
4231 
4232         date.yearBC = 10;
4233         assert(date == Date(-9, 1, 1));
4234     }
4235 
4236     @safe unittest
4237     {
4238         assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
4239 
4240         auto date = Date(0, 7, 6);
4241         const cdate = Date(0, 7, 6);
4242         immutable idate = Date(0, 7, 6);
4243         date.yearBC = 7;
4244         assert(date.yearBC == 7);
4245         static assert(!__traits(compiles, cdate.yearBC = 7));
4246         static assert(!__traits(compiles, idate.yearBC = 7));
4247     }
4248 
4249 
4250     /++
4251         Month of a Gregorian Year.
4252      +/
4253     @property Month month() const @safe pure nothrow @nogc
4254     {
4255         return _month;
4256     }
4257 
4258     ///
4259     @safe unittest
4260     {
4261         assert(Date(1999, 7, 6).month == 7);
4262         assert(Date(2010, 10, 4).month == 10);
4263         assert(Date(-7, 4, 5).month == 4);
4264     }
4265 
4266     @safe unittest
4267     {
4268         assert(Date.init.month == 1);
4269         assert(Date(1999, 7, 6).month == 7);
4270         assert(Date(-1999, 7, 6).month == 7);
4271 
4272         const cdate = Date(1999, 7, 6);
4273         immutable idate = Date(1999, 7, 6);
4274         assert(cdate.month == 7);
4275         assert(idate.month == 7);
4276     }
4277 
4278     /++
4279         Month of a Gregorian Year.
4280 
4281         Params:
4282             month = The month to set this $(LREF Date)'s month to.
4283 
4284         Throws:
4285             $(REF DateTimeException,std,datetime,date) if the given month is
4286             not a valid month or if the current day would not be valid in the
4287             given month.
4288      +/
4289     @property void month(Month month) @safe pure
4290     {
4291         enforceValid!"months"(month);
4292         enforceValid!"days"(_year, month, _day);
4293         _month = cast(Month) month;
4294     }
4295 
4296     @safe unittest
4297     {
4298         static void testDate(Date date, Month month, Date expected = Date.init)
4299         {
4300             date.month = month;
4301             assert(expected != Date.init);
4302             assert(date == expected);
4303         }
4304 
4305         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
4306         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
4307         assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
4308         assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
4309 
4310         testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
4311         testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
4312 
4313         const cdate = Date(1999, 7, 6);
4314         immutable idate = Date(1999, 7, 6);
4315         static assert(!__traits(compiles, cdate.month = 7));
4316         static assert(!__traits(compiles, idate.month = 7));
4317     }
4318 
4319 
4320     /++
4321         Day of a Gregorian Month.
4322      +/
4323     @property ubyte day() const @safe pure nothrow @nogc
4324     {
4325         return _day;
4326     }
4327 
4328     ///
4329     @safe unittest
4330     {
4331         assert(Date(1999, 7, 6).day == 6);
4332         assert(Date(2010, 10, 4).day == 4);
4333         assert(Date(-7, 4, 5).day == 5);
4334     }
4335 
4336     @safe unittest
4337     {
4338         import std.format : format;
4339         import std.range : chain;
4340 
4341         static void test(Date date, int expected)
4342         {
4343             assert(date.day == expected, format("Value given: %s", date));
4344         }
4345 
4346         foreach (year; chain(testYearsBC, testYearsAD))
4347         {
4348             foreach (md; testMonthDays)
4349                 test(Date(year, md.month, md.day), md.day);
4350         }
4351 
4352         const cdate = Date(1999, 7, 6);
4353         immutable idate = Date(1999, 7, 6);
4354         assert(cdate.day == 6);
4355         assert(idate.day == 6);
4356     }
4357 
4358     /++
4359         Day of a Gregorian Month.
4360 
4361         Params:
4362             day = The day of the month to set this $(LREF Date)'s day to.
4363 
4364         Throws:
4365             $(REF DateTimeException,std,datetime,date) if the given day is not
4366             a valid day of the current month.
4367      +/
4368     @property void day(int day) @safe pure
4369     {
4370         enforceValid!"days"(_year, _month, day);
4371         _day = cast(ubyte) day;
4372     }
4373 
4374     @safe unittest
4375     {
4376         import std.exception : assertNotThrown;
4377 
4378         static void testDate(Date date, int day)
4379         {
4380             date.day = day;
4381         }
4382 
4383         // Test A.D.
4384         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
4385         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
4386         assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
4387         assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
4388         assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
4389         assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
4390         assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
4391         assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
4392         assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
4393         assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
4394         assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
4395         assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
4396         assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
4397         assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
4398 
4399         assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
4400         assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
4401         assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
4402         assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
4403         assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
4404         assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
4405         assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
4406         assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
4407         assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
4408         assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
4409         assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
4410         assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
4411         assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
4412 
4413         {
4414             auto date = Date(1, 1, 1);
4415             date.day = 6;
4416             assert(date == Date(1, 1, 6));
4417         }
4418 
4419         // Test B.C.
4420         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
4421         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
4422         assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
4423         assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
4424         assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
4425         assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
4426         assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
4427         assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
4428         assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
4429         assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
4430         assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
4431         assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
4432         assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
4433         assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
4434 
4435         assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
4436         assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
4437         assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
4438         assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
4439         assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
4440         assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
4441         assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
4442         assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
4443         assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
4444         assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
4445         assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
4446         assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
4447         assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
4448 
4449         {
4450             auto date = Date(-1, 1, 1);
4451             date.day = 6;
4452             assert(date == Date(-1, 1, 6));
4453         }
4454 
4455         const cdate = Date(1999, 7, 6);
4456         immutable idate = Date(1999, 7, 6);
4457         static assert(!__traits(compiles, cdate.day = 6));
4458         static assert(!__traits(compiles, idate.day = 6));
4459     }
4460 
4461 
4462     /++
4463         Adds the given number of years or months to this $(LREF Date), mutating
4464         it. A negative number will subtract.
4465 
4466         Note that if day overflow is allowed, and the date with the adjusted
4467         year/month overflows the number of days in the new month, then the month
4468         will be incremented by one, and the day set to the number of days
4469         overflowed. (e.g. if the day were 31 and the new month were June, then
4470         the month would be incremented to July, and the new day would be 1). If
4471         day overflow is not allowed, then the day will be set to the last valid
4472         day in the month (e.g. June 31st would become June 30th).
4473 
4474         Params:
4475             units         = The type of units to add ("years" or "months").
4476             value         = The number of months or years to add to this
4477                             $(LREF Date).
4478             allowOverflow = Whether the day should be allowed to overflow,
4479                             causing the month to increment.
4480 
4481         Returns:
4482             A reference to the `Date` (`this`).
4483       +/
4484     @safe pure nothrow @nogc
4485     ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4486         if (units == "years")
4487     {
4488         _year += value;
4489 
4490         if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
4491         {
4492             if (allowOverflow == AllowDayOverflow.yes)
4493             {
4494                 _month = Month.mar;
4495                 _day = 1;
4496             }
4497             else
4498                 _day = 28;
4499         }
4500 
4501         return this;
4502     }
4503 
4504     ///
4505     @safe unittest
4506     {
4507         auto d1 = Date(2010, 1, 1);
4508         d1.add!"months"(11);
4509         assert(d1 == Date(2010, 12, 1));
4510 
4511         auto d2 = Date(2010, 1, 1);
4512         d2.add!"months"(-11);
4513         assert(d2 == Date(2009, 2, 1));
4514 
4515         auto d3 = Date(2000, 2, 29);
4516         d3.add!"years"(1);
4517         assert(d3 == Date(2001, 3, 1));
4518 
4519         auto d4 = Date(2000, 2, 29);
4520         d4.add!"years"(1, AllowDayOverflow.no);
4521         assert(d4 == Date(2001, 2, 28));
4522     }
4523 
4524     // Test add!"years"() with AllowDayOverflow.yes
4525     @safe unittest
4526     {
4527         // Test A.D.
4528         {
4529             auto date = Date(1999, 7, 6);
4530             date.add!"years"(7);
4531             assert(date == Date(2006, 7, 6));
4532             date.add!"years"(-9);
4533             assert(date == Date(1997, 7, 6));
4534         }
4535 
4536         {
4537             auto date = Date(1999, 2, 28);
4538             date.add!"years"(1);
4539             assert(date == Date(2000, 2, 28));
4540         }
4541 
4542         {
4543             auto date = Date(2000, 2, 29);
4544             date.add!"years"(-1);
4545             assert(date == Date(1999, 3, 1));
4546         }
4547 
4548         // Test B.C.
4549         {
4550             auto date = Date(-1999, 7, 6);
4551             date.add!"years"(-7);
4552             assert(date == Date(-2006, 7, 6));
4553             date.add!"years"(9);
4554             assert(date == Date(-1997, 7, 6));
4555         }
4556 
4557         {
4558             auto date = Date(-1999, 2, 28);
4559             date.add!"years"(-1);
4560             assert(date == Date(-2000, 2, 28));
4561         }
4562 
4563         {
4564             auto date = Date(-2000, 2, 29);
4565             date.add!"years"(1);
4566             assert(date == Date(-1999, 3, 1));
4567         }
4568 
4569         // Test Both
4570         {
4571             auto date = Date(4, 7, 6);
4572             date.add!"years"(-5);
4573             assert(date == Date(-1, 7, 6));
4574             date.add!"years"(5);
4575             assert(date == Date(4, 7, 6));
4576         }
4577 
4578         {
4579             auto date = Date(-4, 7, 6);
4580             date.add!"years"(5);
4581             assert(date == Date(1, 7, 6));
4582             date.add!"years"(-5);
4583             assert(date == Date(-4, 7, 6));
4584         }
4585 
4586         {
4587             auto date = Date(4, 7, 6);
4588             date.add!"years"(-8);
4589             assert(date == Date(-4, 7, 6));
4590             date.add!"years"(8);
4591             assert(date == Date(4, 7, 6));
4592         }
4593 
4594         {
4595             auto date = Date(-4, 7, 6);
4596             date.add!"years"(8);
4597             assert(date == Date(4, 7, 6));
4598             date.add!"years"(-8);
4599             assert(date == Date(-4, 7, 6));
4600         }
4601 
4602         {
4603             auto date = Date(-4, 2, 29);
4604             date.add!"years"(5);
4605             assert(date == Date(1, 3, 1));
4606         }
4607 
4608         {
4609             auto date = Date(4, 2, 29);
4610             date.add!"years"(-5);
4611             assert(date == Date(-1, 3, 1));
4612         }
4613 
4614         {
4615             auto date = Date(4, 2, 29);
4616             date.add!"years"(-5).add!"years"(7);
4617             assert(date == Date(6, 3, 1));
4618         }
4619 
4620         const cdate = Date(1999, 7, 6);
4621         immutable idate = Date(1999, 7, 6);
4622         static assert(!__traits(compiles, cdate.add!"years"(7)));
4623         static assert(!__traits(compiles, idate.add!"years"(7)));
4624     }
4625 
4626     // Test add!"years"() with AllowDayOverflow.no
4627     @safe unittest
4628     {
4629         // Test A.D.
4630         {
4631             auto date = Date(1999, 7, 6);
4632             date.add!"years"(7, AllowDayOverflow.no);
4633             assert(date == Date(2006, 7, 6));
4634             date.add!"years"(-9, AllowDayOverflow.no);
4635             assert(date == Date(1997, 7, 6));
4636         }
4637 
4638         {
4639             auto date = Date(1999, 2, 28);
4640             date.add!"years"(1, AllowDayOverflow.no);
4641             assert(date == Date(2000, 2, 28));
4642         }
4643 
4644         {
4645             auto date = Date(2000, 2, 29);
4646             date.add!"years"(-1, AllowDayOverflow.no);
4647             assert(date == Date(1999, 2, 28));
4648         }
4649 
4650         // Test B.C.
4651         {
4652             auto date = Date(-1999, 7, 6);
4653             date.add!"years"(-7, AllowDayOverflow.no);
4654             assert(date == Date(-2006, 7, 6));
4655             date.add!"years"(9, AllowDayOverflow.no);
4656             assert(date == Date(-1997, 7, 6));
4657         }
4658 
4659         {
4660             auto date = Date(-1999, 2, 28);
4661             date.add!"years"(-1, AllowDayOverflow.no);
4662             assert(date == Date(-2000, 2, 28));
4663         }
4664 
4665         {
4666             auto date = Date(-2000, 2, 29);
4667             date.add!"years"(1, AllowDayOverflow.no);
4668             assert(date == Date(-1999, 2, 28));
4669         }
4670 
4671         // Test Both
4672         {
4673             auto date = Date(4, 7, 6);
4674             date.add!"years"(-5, AllowDayOverflow.no);
4675             assert(date == Date(-1, 7, 6));
4676             date.add!"years"(5, AllowDayOverflow.no);
4677             assert(date == Date(4, 7, 6));
4678         }
4679 
4680         {
4681             auto date = Date(-4, 7, 6);
4682             date.add!"years"(5, AllowDayOverflow.no);
4683             assert(date == Date(1, 7, 6));
4684             date.add!"years"(-5, AllowDayOverflow.no);
4685             assert(date == Date(-4, 7, 6));
4686         }
4687 
4688         {
4689             auto date = Date(4, 7, 6);
4690             date.add!"years"(-8, AllowDayOverflow.no);
4691             assert(date == Date(-4, 7, 6));
4692             date.add!"years"(8, AllowDayOverflow.no);
4693             assert(date == Date(4, 7, 6));
4694         }
4695 
4696         {
4697             auto date = Date(-4, 7, 6);
4698             date.add!"years"(8, AllowDayOverflow.no);
4699             assert(date == Date(4, 7, 6));
4700             date.add!"years"(-8, AllowDayOverflow.no);
4701             assert(date == Date(-4, 7, 6));
4702         }
4703 
4704         {
4705             auto date = Date(-4, 2, 29);
4706             date.add!"years"(5, AllowDayOverflow.no);
4707             assert(date == Date(1, 2, 28));
4708         }
4709 
4710         {
4711             auto date = Date(4, 2, 29);
4712             date.add!"years"(-5, AllowDayOverflow.no);
4713             assert(date == Date(-1, 2, 28));
4714         }
4715 
4716         {
4717             auto date = Date(4, 2, 29);
4718             date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
4719             assert(date == Date(6, 2, 28));
4720         }
4721     }
4722 
4723 
4724     // Shares documentation with "years" version.
4725     @safe pure nothrow @nogc
4726     ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4727         if (units == "months")
4728     {
4729         auto years = months / 12;
4730         months %= 12;
4731         auto newMonth = _month + months;
4732 
4733         if (months < 0)
4734         {
4735             if (newMonth < 1)
4736             {
4737                 newMonth += 12;
4738                 --years;
4739             }
4740         }
4741         else if (newMonth > 12)
4742         {
4743             newMonth -= 12;
4744             ++years;
4745         }
4746 
4747         _year += years;
4748         _month = cast(Month) newMonth;
4749 
4750         immutable currMaxDay = maxDay(_year, _month);
4751         immutable overflow = _day - currMaxDay;
4752 
4753         if (overflow > 0)
4754         {
4755             if (allowOverflow == AllowDayOverflow.yes)
4756             {
4757                 ++_month;
4758                 _day = cast(ubyte) overflow;
4759             }
4760             else
4761                 _day = cast(ubyte) currMaxDay;
4762         }
4763 
4764         return this;
4765     }
4766 
4767     // Test add!"months"() with AllowDayOverflow.yes
4768     @safe unittest
4769     {
4770         // Test A.D.
4771         {
4772             auto date = Date(1999, 7, 6);
4773             date.add!"months"(3);
4774             assert(date == Date(1999, 10, 6));
4775             date.add!"months"(-4);
4776             assert(date == Date(1999, 6, 6));
4777         }
4778 
4779         {
4780             auto date = Date(1999, 7, 6);
4781             date.add!"months"(6);
4782             assert(date == Date(2000, 1, 6));
4783             date.add!"months"(-6);
4784             assert(date == Date(1999, 7, 6));
4785         }
4786 
4787         {
4788             auto date = Date(1999, 7, 6);
4789             date.add!"months"(27);
4790             assert(date == Date(2001, 10, 6));
4791             date.add!"months"(-28);
4792             assert(date == Date(1999, 6, 6));
4793         }
4794 
4795         {
4796             auto date = Date(1999, 5, 31);
4797             date.add!"months"(1);
4798             assert(date == Date(1999, 7, 1));
4799         }
4800 
4801         {
4802             auto date = Date(1999, 5, 31);
4803             date.add!"months"(-1);
4804             assert(date == Date(1999, 5, 1));
4805         }
4806 
4807         {
4808             auto date = Date(1999, 2, 28);
4809             date.add!"months"(12);
4810             assert(date == Date(2000, 2, 28));
4811         }
4812 
4813         {
4814             auto date = Date(2000, 2, 29);
4815             date.add!"months"(12);
4816             assert(date == Date(2001, 3, 1));
4817         }
4818 
4819         {
4820             auto date = Date(1999, 7, 31);
4821             date.add!"months"(1);
4822             assert(date == Date(1999, 8, 31));
4823             date.add!"months"(1);
4824             assert(date == Date(1999, 10, 1));
4825         }
4826 
4827         {
4828             auto date = Date(1998, 8, 31);
4829             date.add!"months"(13);
4830             assert(date == Date(1999, 10, 1));
4831             date.add!"months"(-13);
4832             assert(date == Date(1998, 9, 1));
4833         }
4834 
4835         {
4836             auto date = Date(1997, 12, 31);
4837             date.add!"months"(13);
4838             assert(date == Date(1999, 1, 31));
4839             date.add!"months"(-13);
4840             assert(date == Date(1997, 12, 31));
4841         }
4842 
4843         {
4844             auto date = Date(1997, 12, 31);
4845             date.add!"months"(14);
4846             assert(date == Date(1999, 3, 3));
4847             date.add!"months"(-14);
4848             assert(date == Date(1998, 1, 3));
4849         }
4850 
4851         {
4852             auto date = Date(1998, 12, 31);
4853             date.add!"months"(14);
4854             assert(date == Date(2000, 3, 2));
4855             date.add!"months"(-14);
4856             assert(date == Date(1999, 1, 2));
4857         }
4858 
4859         {
4860             auto date = Date(1999, 12, 31);
4861             date.add!"months"(14);
4862             assert(date == Date(2001, 3, 3));
4863             date.add!"months"(-14);
4864             assert(date == Date(2000, 1, 3));
4865         }
4866 
4867         // Test B.C.
4868         {
4869             auto date = Date(-1999, 7, 6);
4870             date.add!"months"(3);
4871             assert(date == Date(-1999, 10, 6));
4872             date.add!"months"(-4);
4873             assert(date == Date(-1999, 6, 6));
4874         }
4875 
4876         {
4877             auto date = Date(-1999, 7, 6);
4878             date.add!"months"(6);
4879             assert(date == Date(-1998, 1, 6));
4880             date.add!"months"(-6);
4881             assert(date == Date(-1999, 7, 6));
4882         }
4883 
4884         {
4885             auto date = Date(-1999, 7, 6);
4886             date.add!"months"(-27);
4887             assert(date == Date(-2001, 4, 6));
4888             date.add!"months"(28);
4889             assert(date == Date(-1999, 8, 6));
4890         }
4891 
4892         {
4893             auto date = Date(-1999, 5, 31);
4894             date.add!"months"(1);
4895             assert(date == Date(-1999, 7, 1));
4896         }
4897 
4898         {
4899             auto date = Date(-1999, 5, 31);
4900             date.add!"months"(-1);
4901             assert(date == Date(-1999, 5, 1));
4902         }
4903 
4904         {
4905             auto date = Date(-1999, 2, 28);
4906             date.add!"months"(-12);
4907             assert(date == Date(-2000, 2, 28));
4908         }
4909 
4910         {
4911             auto date = Date(-2000, 2, 29);
4912             date.add!"months"(-12);
4913             assert(date == Date(-2001, 3, 1));
4914         }
4915 
4916         {
4917             auto date = Date(-1999, 7, 31);
4918             date.add!"months"(1);
4919             assert(date == Date(-1999, 8, 31));
4920             date.add!"months"(1);
4921             assert(date == Date(-1999, 10, 1));
4922         }
4923 
4924         {
4925             auto date = Date(-1998, 8, 31);
4926             date.add!"months"(13);
4927             assert(date == Date(-1997, 10, 1));
4928             date.add!"months"(-13);
4929             assert(date == Date(-1998, 9, 1));
4930         }
4931 
4932         {
4933             auto date = Date(-1997, 12, 31);
4934             date.add!"months"(13);
4935             assert(date == Date(-1995, 1, 31));
4936             date.add!"months"(-13);
4937             assert(date == Date(-1997, 12, 31));
4938         }
4939 
4940         {
4941             auto date = Date(-1997, 12, 31);
4942             date.add!"months"(14);
4943             assert(date == Date(-1995, 3, 3));
4944             date.add!"months"(-14);
4945             assert(date == Date(-1996, 1, 3));
4946         }
4947 
4948         {
4949             auto date = Date(-2002, 12, 31);
4950             date.add!"months"(14);
4951             assert(date == Date(-2000, 3, 2));
4952             date.add!"months"(-14);
4953             assert(date == Date(-2001, 1, 2));
4954         }
4955 
4956         {
4957             auto date = Date(-2001, 12, 31);
4958             date.add!"months"(14);
4959             assert(date == Date(-1999, 3, 3));
4960             date.add!"months"(-14);
4961             assert(date == Date(-2000, 1, 3));
4962         }
4963 
4964         // Test Both
4965         {
4966             auto date = Date(1, 1, 1);
4967             date.add!"months"(-1);
4968             assert(date == Date(0, 12, 1));
4969             date.add!"months"(1);
4970             assert(date == Date(1, 1, 1));
4971         }
4972 
4973         {
4974             auto date = Date(4, 1, 1);
4975             date.add!"months"(-48);
4976             assert(date == Date(0, 1, 1));
4977             date.add!"months"(48);
4978             assert(date == Date(4, 1, 1));
4979         }
4980 
4981         {
4982             auto date = Date(4, 3, 31);
4983             date.add!"months"(-49);
4984             assert(date == Date(0, 3, 2));
4985             date.add!"months"(49);
4986             assert(date == Date(4, 4, 2));
4987         }
4988 
4989         {
4990             auto date = Date(4, 3, 31);
4991             date.add!"months"(-85);
4992             assert(date == Date(-3, 3, 3));
4993             date.add!"months"(85);
4994             assert(date == Date(4, 4, 3));
4995         }
4996 
4997         {
4998             auto date = Date(-3, 3, 31);
4999             date.add!"months"(85).add!"months"(-83);
5000             assert(date == Date(-3, 6, 1));
5001         }
5002 
5003         const cdate = Date(1999, 7, 6);
5004         immutable idate = Date(1999, 7, 6);
5005         static assert(!__traits(compiles, cdate.add!"months"(3)));
5006         static assert(!__traits(compiles, idate.add!"months"(3)));
5007     }
5008 
5009     // Test add!"months"() with AllowDayOverflow.no
5010     @safe unittest
5011     {
5012         // Test A.D.
5013         {
5014             auto date = Date(1999, 7, 6);
5015             date.add!"months"(3, AllowDayOverflow.no);
5016             assert(date == Date(1999, 10, 6));
5017             date.add!"months"(-4, AllowDayOverflow.no);
5018             assert(date == Date(1999, 6, 6));
5019         }
5020 
5021         {
5022             auto date = Date(1999, 7, 6);
5023             date.add!"months"(6, AllowDayOverflow.no);
5024             assert(date == Date(2000, 1, 6));
5025             date.add!"months"(-6, AllowDayOverflow.no);
5026             assert(date == Date(1999, 7, 6));
5027         }
5028 
5029         {
5030             auto date = Date(1999, 7, 6);
5031             date.add!"months"(27, AllowDayOverflow.no);
5032             assert(date == Date(2001, 10, 6));
5033             date.add!"months"(-28, AllowDayOverflow.no);
5034             assert(date == Date(1999, 6, 6));
5035         }
5036 
5037         {
5038             auto date = Date(1999, 5, 31);
5039             date.add!"months"(1, AllowDayOverflow.no);
5040             assert(date == Date(1999, 6, 30));
5041         }
5042 
5043         {
5044             auto date = Date(1999, 5, 31);
5045             date.add!"months"(-1, AllowDayOverflow.no);
5046             assert(date == Date(1999, 4, 30));
5047         }
5048 
5049         {
5050             auto date = Date(1999, 2, 28);
5051             date.add!"months"(12, AllowDayOverflow.no);
5052             assert(date == Date(2000, 2, 28));
5053         }
5054 
5055         {
5056             auto date = Date(2000, 2, 29);
5057             date.add!"months"(12, AllowDayOverflow.no);
5058             assert(date == Date(2001, 2, 28));
5059         }
5060 
5061         {
5062             auto date = Date(1999, 7, 31);
5063             date.add!"months"(1, AllowDayOverflow.no);
5064             assert(date == Date(1999, 8, 31));
5065             date.add!"months"(1, AllowDayOverflow.no);
5066             assert(date == Date(1999, 9, 30));
5067         }
5068 
5069         {
5070             auto date = Date(1998, 8, 31);
5071             date.add!"months"(13, AllowDayOverflow.no);
5072             assert(date == Date(1999, 9, 30));
5073             date.add!"months"(-13, AllowDayOverflow.no);
5074             assert(date == Date(1998, 8, 30));
5075         }
5076 
5077         {
5078             auto date = Date(1997, 12, 31);
5079             date.add!"months"(13, AllowDayOverflow.no);
5080             assert(date == Date(1999, 1, 31));
5081             date.add!"months"(-13, AllowDayOverflow.no);
5082             assert(date == Date(1997, 12, 31));
5083         }
5084 
5085         {
5086             auto date = Date(1997, 12, 31);
5087             date.add!"months"(14, AllowDayOverflow.no);
5088             assert(date == Date(1999, 2, 28));
5089             date.add!"months"(-14, AllowDayOverflow.no);
5090             assert(date == Date(1997, 12, 28));
5091         }
5092 
5093         {
5094             auto date = Date(1998, 12, 31);
5095             date.add!"months"(14, AllowDayOverflow.no);
5096             assert(date == Date(2000, 2, 29));
5097             date.add!"months"(-14, AllowDayOverflow.no);
5098             assert(date == Date(1998, 12, 29));
5099         }
5100 
5101         {
5102             auto date = Date(1999, 12, 31);
5103             date.add!"months"(14, AllowDayOverflow.no);
5104             assert(date == Date(2001, 2, 28));
5105             date.add!"months"(-14, AllowDayOverflow.no);
5106             assert(date == Date(1999, 12, 28));
5107         }
5108 
5109         // Test B.C.
5110         {
5111             auto date = Date(-1999, 7, 6);
5112             date.add!"months"(3, AllowDayOverflow.no);
5113             assert(date == Date(-1999, 10, 6));
5114             date.add!"months"(-4, AllowDayOverflow.no);
5115             assert(date == Date(-1999, 6, 6));
5116         }
5117 
5118         {
5119             auto date = Date(-1999, 7, 6);
5120             date.add!"months"(6, AllowDayOverflow.no);
5121             assert(date == Date(-1998, 1, 6));
5122             date.add!"months"(-6, AllowDayOverflow.no);
5123             assert(date == Date(-1999, 7, 6));
5124         }
5125 
5126         {
5127             auto date = Date(-1999, 7, 6);
5128             date.add!"months"(-27, AllowDayOverflow.no);
5129             assert(date == Date(-2001, 4, 6));
5130             date.add!"months"(28, AllowDayOverflow.no);
5131             assert(date == Date(-1999, 8, 6));
5132         }
5133 
5134         {
5135             auto date = Date(-1999, 5, 31);
5136             date.add!"months"(1, AllowDayOverflow.no);
5137             assert(date == Date(-1999, 6, 30));
5138         }
5139 
5140         {
5141             auto date = Date(-1999, 5, 31);
5142             date.add!"months"(-1, AllowDayOverflow.no);
5143             assert(date == Date(-1999, 4, 30));
5144         }
5145 
5146         {
5147             auto date = Date(-1999, 2, 28);
5148             date.add!"months"(-12, AllowDayOverflow.no);
5149             assert(date == Date(-2000, 2, 28));
5150         }
5151 
5152         {
5153             auto date = Date(-2000, 2, 29);
5154             date.add!"months"(-12, AllowDayOverflow.no);
5155             assert(date == Date(-2001, 2, 28));
5156         }
5157 
5158         {
5159             auto date = Date(-1999, 7, 31);
5160             date.add!"months"(1, AllowDayOverflow.no);
5161             assert(date == Date(-1999, 8, 31));
5162             date.add!"months"(1, AllowDayOverflow.no);
5163             assert(date == Date(-1999, 9, 30));
5164         }
5165 
5166         {
5167             auto date = Date(-1998, 8, 31);
5168             date.add!"months"(13, AllowDayOverflow.no);
5169             assert(date == Date(-1997, 9, 30));
5170             date.add!"months"(-13, AllowDayOverflow.no);
5171             assert(date == Date(-1998, 8, 30));
5172         }
5173 
5174         {
5175             auto date = Date(-1997, 12, 31);
5176             date.add!"months"(13, AllowDayOverflow.no);
5177             assert(date == Date(-1995, 1, 31));
5178             date.add!"months"(-13, AllowDayOverflow.no);
5179             assert(date == Date(-1997, 12, 31));
5180         }
5181 
5182         {
5183             auto date = Date(-1997, 12, 31);
5184             date.add!"months"(14, AllowDayOverflow.no);
5185             assert(date == Date(-1995, 2, 28));
5186             date.add!"months"(-14, AllowDayOverflow.no);
5187             assert(date == Date(-1997, 12, 28));
5188         }
5189 
5190         {
5191             auto date = Date(-2002, 12, 31);
5192             date.add!"months"(14, AllowDayOverflow.no);
5193             assert(date == Date(-2000, 2, 29));
5194             date.add!"months"(-14, AllowDayOverflow.no);
5195             assert(date == Date(-2002, 12, 29));
5196         }
5197 
5198         {
5199             auto date = Date(-2001, 12, 31);
5200             date.add!"months"(14, AllowDayOverflow.no);
5201             assert(date == Date(-1999, 2, 28));
5202             date.add!"months"(-14, AllowDayOverflow.no);
5203             assert(date == Date(-2001, 12, 28));
5204         }
5205 
5206         // Test Both
5207         {
5208             auto date = Date(1, 1, 1);
5209             date.add!"months"(-1, AllowDayOverflow.no);
5210             assert(date == Date(0, 12, 1));
5211             date.add!"months"(1, AllowDayOverflow.no);
5212             assert(date == Date(1, 1, 1));
5213         }
5214 
5215         {
5216             auto date = Date(4, 1, 1);
5217             date.add!"months"(-48, AllowDayOverflow.no);
5218             assert(date == Date(0, 1, 1));
5219             date.add!"months"(48, AllowDayOverflow.no);
5220             assert(date == Date(4, 1, 1));
5221         }
5222 
5223         {
5224             auto date = Date(4, 3, 31);
5225             date.add!"months"(-49, AllowDayOverflow.no);
5226             assert(date == Date(0, 2, 29));
5227             date.add!"months"(49, AllowDayOverflow.no);
5228             assert(date == Date(4, 3, 29));
5229         }
5230 
5231         {
5232             auto date = Date(4, 3, 31);
5233             date.add!"months"(-85, AllowDayOverflow.no);
5234             assert(date == Date(-3, 2, 28));
5235             date.add!"months"(85, AllowDayOverflow.no);
5236             assert(date == Date(4, 3, 28));
5237         }
5238 
5239         {
5240             auto date = Date(-3, 3, 31);
5241             date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
5242             assert(date == Date(-3, 5, 30));
5243         }
5244     }
5245 
5246 
5247     /++
5248         Adds the given number of years or months to this $(LREF Date), mutating
5249         it. A negative number will subtract.
5250 
5251         The difference between rolling and adding is that rolling does not
5252         affect larger units. Rolling a $(LREF Date) 12 months gets
5253         the exact same $(LREF Date). However, the days can still be affected due
5254         to the differing number of days in each month.
5255 
5256         Because there are no units larger than years, there is no difference
5257         between adding and rolling years.
5258 
5259         Params:
5260             units         = The type of units to add ("years" or "months").
5261             value         = The number of months or years to add to this
5262                             $(LREF Date).
5263             allowOverflow = Whether the day should be allowed to overflow,
5264                             causing the month to increment.
5265 
5266         Returns:
5267             A reference to the `Date` (`this`).
5268       +/
5269     @safe pure nothrow @nogc
5270     ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5271         if (units == "years")
5272     {
5273         return add!"years"(value, allowOverflow);
5274     }
5275 
5276     ///
5277     @safe unittest
5278     {
5279         auto d1 = Date(2010, 1, 1);
5280         d1.roll!"months"(1);
5281         assert(d1 == Date(2010, 2, 1));
5282 
5283         auto d2 = Date(2010, 1, 1);
5284         d2.roll!"months"(-1);
5285         assert(d2 == Date(2010, 12, 1));
5286 
5287         auto d3 = Date(1999, 1, 29);
5288         d3.roll!"months"(1);
5289         assert(d3 == Date(1999, 3, 1));
5290 
5291         auto d4 = Date(1999, 1, 29);
5292         d4.roll!"months"(1, AllowDayOverflow.no);
5293         assert(d4 == Date(1999, 2, 28));
5294 
5295         auto d5 = Date(2000, 2, 29);
5296         d5.roll!"years"(1);
5297         assert(d5 == Date(2001, 3, 1));
5298 
5299         auto d6 = Date(2000, 2, 29);
5300         d6.roll!"years"(1, AllowDayOverflow.no);
5301         assert(d6 == Date(2001, 2, 28));
5302     }
5303 
5304     @safe unittest
5305     {
5306         const cdate = Date(1999, 7, 6);
5307         immutable idate = Date(1999, 7, 6);
5308         static assert(!__traits(compiles, cdate.roll!"years"(3)));
5309         static assert(!__traits(compiles, idate.rolYears(3)));
5310     }
5311 
5312 
5313     // Shares documentation with "years" version.
5314     @safe pure nothrow @nogc
5315     ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5316         if (units == "months")
5317     {
5318         months %= 12;
5319         auto newMonth = _month + months;
5320 
5321         if (months < 0)
5322         {
5323             if (newMonth < 1)
5324                 newMonth += 12;
5325         }
5326         else
5327         {
5328             if (newMonth > 12)
5329                 newMonth -= 12;
5330         }
5331 
5332         _month = cast(Month) newMonth;
5333 
5334         immutable currMaxDay = maxDay(_year, _month);
5335         immutable overflow = _day - currMaxDay;
5336 
5337         if (overflow > 0)
5338         {
5339             if (allowOverflow == AllowDayOverflow.yes)
5340             {
5341                 ++_month;
5342                 _day = cast(ubyte) overflow;
5343             }
5344             else
5345                 _day = cast(ubyte) currMaxDay;
5346         }
5347 
5348         return this;
5349     }
5350 
5351     // Test roll!"months"() with AllowDayOverflow.yes
5352     @safe unittest
5353     {
5354         // Test A.D.
5355         {
5356             auto date = Date(1999, 7, 6);
5357             date.roll!"months"(3);
5358             assert(date == Date(1999, 10, 6));
5359             date.roll!"months"(-4);
5360             assert(date == Date(1999, 6, 6));
5361         }
5362 
5363         {
5364             auto date = Date(1999, 7, 6);
5365             date.roll!"months"(6);
5366             assert(date == Date(1999, 1, 6));
5367             date.roll!"months"(-6);
5368             assert(date == Date(1999, 7, 6));
5369         }
5370 
5371         {
5372             auto date = Date(1999, 7, 6);
5373             date.roll!"months"(27);
5374             assert(date == Date(1999, 10, 6));
5375             date.roll!"months"(-28);
5376             assert(date == Date(1999, 6, 6));
5377         }
5378 
5379         {
5380             auto date = Date(1999, 5, 31);
5381             date.roll!"months"(1);
5382             assert(date == Date(1999, 7, 1));
5383         }
5384 
5385         {
5386             auto date = Date(1999, 5, 31);
5387             date.roll!"months"(-1);
5388             assert(date == Date(1999, 5, 1));
5389         }
5390 
5391         {
5392             auto date = Date(1999, 2, 28);
5393             date.roll!"months"(12);
5394             assert(date == Date(1999, 2, 28));
5395         }
5396 
5397         {
5398             auto date = Date(2000, 2, 29);
5399             date.roll!"months"(12);
5400             assert(date == Date(2000, 2, 29));
5401         }
5402 
5403         {
5404             auto date = Date(1999, 7, 31);
5405             date.roll!"months"(1);
5406             assert(date == Date(1999, 8, 31));
5407             date.roll!"months"(1);
5408             assert(date == Date(1999, 10, 1));
5409         }
5410 
5411         {
5412             auto date = Date(1998, 8, 31);
5413             date.roll!"months"(13);
5414             assert(date == Date(1998, 10, 1));
5415             date.roll!"months"(-13);
5416             assert(date == Date(1998, 9, 1));
5417         }
5418 
5419         {
5420             auto date = Date(1997, 12, 31);
5421             date.roll!"months"(13);
5422             assert(date == Date(1997, 1, 31));
5423             date.roll!"months"(-13);
5424             assert(date == Date(1997, 12, 31));
5425         }
5426 
5427         {
5428             auto date = Date(1997, 12, 31);
5429             date.roll!"months"(14);
5430             assert(date == Date(1997, 3, 3));
5431             date.roll!"months"(-14);
5432             assert(date == Date(1997, 1, 3));
5433         }
5434 
5435         {
5436             auto date = Date(1998, 12, 31);
5437             date.roll!"months"(14);
5438             assert(date == Date(1998, 3, 3));
5439             date.roll!"months"(-14);
5440             assert(date == Date(1998, 1, 3));
5441         }
5442 
5443         {
5444             auto date = Date(1999, 12, 31);
5445             date.roll!"months"(14);
5446             assert(date == Date(1999, 3, 3));
5447             date.roll!"months"(-14);
5448             assert(date == Date(1999, 1, 3));
5449         }
5450 
5451         // Test B.C.
5452         {
5453             auto date = Date(-1999, 7, 6);
5454             date.roll!"months"(3);
5455             assert(date == Date(-1999, 10, 6));
5456             date.roll!"months"(-4);
5457             assert(date == Date(-1999, 6, 6));
5458         }
5459 
5460         {
5461             auto date = Date(-1999, 7, 6);
5462             date.roll!"months"(6);
5463             assert(date == Date(-1999, 1, 6));
5464             date.roll!"months"(-6);
5465             assert(date == Date(-1999, 7, 6));
5466         }
5467 
5468         {
5469             auto date = Date(-1999, 7, 6);
5470             date.roll!"months"(-27);
5471             assert(date == Date(-1999, 4, 6));
5472             date.roll!"months"(28);
5473             assert(date == Date(-1999, 8, 6));
5474         }
5475 
5476         {
5477             auto date = Date(-1999, 5, 31);
5478             date.roll!"months"(1);
5479             assert(date == Date(-1999, 7, 1));
5480         }
5481 
5482         {
5483             auto date = Date(-1999, 5, 31);
5484             date.roll!"months"(-1);
5485             assert(date == Date(-1999, 5, 1));
5486         }
5487 
5488         {
5489             auto date = Date(-1999, 2, 28);
5490             date.roll!"months"(-12);
5491             assert(date == Date(-1999, 2, 28));
5492         }
5493 
5494         {
5495             auto date = Date(-2000, 2, 29);
5496             date.roll!"months"(-12);
5497             assert(date == Date(-2000, 2, 29));
5498         }
5499 
5500         {
5501             auto date = Date(-1999, 7, 31);
5502             date.roll!"months"(1);
5503             assert(date == Date(-1999, 8, 31));
5504             date.roll!"months"(1);
5505             assert(date == Date(-1999, 10, 1));
5506         }
5507 
5508         {
5509             auto date = Date(-1998, 8, 31);
5510             date.roll!"months"(13);
5511             assert(date == Date(-1998, 10, 1));
5512             date.roll!"months"(-13);
5513             assert(date == Date(-1998, 9, 1));
5514         }
5515 
5516         {
5517             auto date = Date(-1997, 12, 31);
5518             date.roll!"months"(13);
5519             assert(date == Date(-1997, 1, 31));
5520             date.roll!"months"(-13);
5521             assert(date == Date(-1997, 12, 31));
5522         }
5523 
5524         {
5525             auto date = Date(-1997, 12, 31);
5526             date.roll!"months"(14);
5527             assert(date == Date(-1997, 3, 3));
5528             date.roll!"months"(-14);
5529             assert(date == Date(-1997, 1, 3));
5530         }
5531 
5532         {
5533             auto date = Date(-2002, 12, 31);
5534             date.roll!"months"(14);
5535             assert(date == Date(-2002, 3, 3));
5536             date.roll!"months"(-14);
5537             assert(date == Date(-2002, 1, 3));
5538         }
5539 
5540         {
5541             auto date = Date(-2001, 12, 31);
5542             date.roll!"months"(14);
5543             assert(date == Date(-2001, 3, 3));
5544             date.roll!"months"(-14);
5545             assert(date == Date(-2001, 1, 3));
5546         }
5547 
5548         // Test Both
5549         {
5550             auto date = Date(1, 1, 1);
5551             date.roll!"months"(-1);
5552             assert(date == Date(1, 12, 1));
5553             date.roll!"months"(1);
5554             assert(date == Date(1, 1, 1));
5555         }
5556 
5557         {
5558             auto date = Date(4, 1, 1);
5559             date.roll!"months"(-48);
5560             assert(date == Date(4, 1, 1));
5561             date.roll!"months"(48);
5562             assert(date == Date(4, 1, 1));
5563         }
5564 
5565         {
5566             auto date = Date(4, 3, 31);
5567             date.roll!"months"(-49);
5568             assert(date == Date(4, 3, 2));
5569             date.roll!"months"(49);
5570             assert(date == Date(4, 4, 2));
5571         }
5572 
5573         {
5574             auto date = Date(4, 3, 31);
5575             date.roll!"months"(-85);
5576             assert(date == Date(4, 3, 2));
5577             date.roll!"months"(85);
5578             assert(date == Date(4, 4, 2));
5579         }
5580 
5581         {
5582             auto date = Date(-1, 1, 1);
5583             date.roll!"months"(-1);
5584             assert(date == Date(-1, 12, 1));
5585             date.roll!"months"(1);
5586             assert(date == Date(-1, 1, 1));
5587         }
5588 
5589         {
5590             auto date = Date(-4, 1, 1);
5591             date.roll!"months"(-48);
5592             assert(date == Date(-4, 1, 1));
5593             date.roll!"months"(48);
5594             assert(date == Date(-4, 1, 1));
5595         }
5596 
5597         {
5598             auto date = Date(-4, 3, 31);
5599             date.roll!"months"(-49);
5600             assert(date == Date(-4, 3, 2));
5601             date.roll!"months"(49);
5602             assert(date == Date(-4, 4, 2));
5603         }
5604 
5605         {
5606             auto date = Date(-4, 3, 31);
5607             date.roll!"months"(-85);
5608             assert(date == Date(-4, 3, 2));
5609             date.roll!"months"(85);
5610             assert(date == Date(-4, 4, 2));
5611         }
5612 
5613         {
5614             auto date = Date(-3, 3, 31);
5615             date.roll!"months"(85).roll!"months"(-83);
5616             assert(date == Date(-3, 6, 1));
5617         }
5618 
5619         const cdate = Date(1999, 7, 6);
5620         immutable idate = Date(1999, 7, 6);
5621         static assert(!__traits(compiles, cdate.roll!"months"(3)));
5622         static assert(!__traits(compiles, idate.roll!"months"(3)));
5623     }
5624 
5625     // Test roll!"months"() with AllowDayOverflow.no
5626     @safe unittest
5627     {
5628         // Test A.D.
5629         {
5630             auto date = Date(1999, 7, 6);
5631             date.roll!"months"(3, AllowDayOverflow.no);
5632             assert(date == Date(1999, 10, 6));
5633             date.roll!"months"(-4, AllowDayOverflow.no);
5634             assert(date == Date(1999, 6, 6));
5635         }
5636 
5637         {
5638             auto date = Date(1999, 7, 6);
5639             date.roll!"months"(6, AllowDayOverflow.no);
5640             assert(date == Date(1999, 1, 6));
5641             date.roll!"months"(-6, AllowDayOverflow.no);
5642             assert(date == Date(1999, 7, 6));
5643         }
5644 
5645         {
5646             auto date = Date(1999, 7, 6);
5647             date.roll!"months"(27, AllowDayOverflow.no);
5648             assert(date == Date(1999, 10, 6));
5649             date.roll!"months"(-28, AllowDayOverflow.no);
5650             assert(date == Date(1999, 6, 6));
5651         }
5652 
5653         {
5654             auto date = Date(1999, 5, 31);
5655             date.roll!"months"(1, AllowDayOverflow.no);
5656             assert(date == Date(1999, 6, 30));
5657         }
5658 
5659         {
5660             auto date = Date(1999, 5, 31);
5661             date.roll!"months"(-1, AllowDayOverflow.no);
5662             assert(date == Date(1999, 4, 30));
5663         }
5664 
5665         {
5666             auto date = Date(1999, 2, 28);
5667             date.roll!"months"(12, AllowDayOverflow.no);
5668             assert(date == Date(1999, 2, 28));
5669         }
5670 
5671         {
5672             auto date = Date(2000, 2, 29);
5673             date.roll!"months"(12, AllowDayOverflow.no);
5674             assert(date == Date(2000, 2, 29));
5675         }
5676 
5677         {
5678             auto date = Date(1999, 7, 31);
5679             date.roll!"months"(1, AllowDayOverflow.no);
5680             assert(date == Date(1999, 8, 31));
5681             date.roll!"months"(1, AllowDayOverflow.no);
5682             assert(date == Date(1999, 9, 30));
5683         }
5684 
5685         {
5686             auto date = Date(1998, 8, 31);
5687             date.roll!"months"(13, AllowDayOverflow.no);
5688             assert(date == Date(1998, 9, 30));
5689             date.roll!"months"(-13, AllowDayOverflow.no);
5690             assert(date == Date(1998, 8, 30));
5691         }
5692 
5693         {
5694             auto date = Date(1997, 12, 31);
5695             date.roll!"months"(13, AllowDayOverflow.no);
5696             assert(date == Date(1997, 1, 31));
5697             date.roll!"months"(-13, AllowDayOverflow.no);
5698             assert(date == Date(1997, 12, 31));
5699         }
5700 
5701         {
5702             auto date = Date(1997, 12, 31);
5703             date.roll!"months"(14, AllowDayOverflow.no);
5704             assert(date == Date(1997, 2, 28));
5705             date.roll!"months"(-14, AllowDayOverflow.no);
5706             assert(date == Date(1997, 12, 28));
5707         }
5708 
5709         {
5710             auto date = Date(1998, 12, 31);
5711             date.roll!"months"(14, AllowDayOverflow.no);
5712             assert(date == Date(1998, 2, 28));
5713             date.roll!"months"(-14, AllowDayOverflow.no);
5714             assert(date == Date(1998, 12, 28));
5715         }
5716 
5717         {
5718             auto date = Date(1999, 12, 31);
5719             date.roll!"months"(14, AllowDayOverflow.no);
5720             assert(date == Date(1999, 2, 28));
5721             date.roll!"months"(-14, AllowDayOverflow.no);
5722             assert(date == Date(1999, 12, 28));
5723         }
5724 
5725         // Test B.C.
5726         {
5727             auto date = Date(-1999, 7, 6);
5728             date.roll!"months"(3, AllowDayOverflow.no);
5729             assert(date == Date(-1999, 10, 6));
5730             date.roll!"months"(-4, AllowDayOverflow.no);
5731             assert(date == Date(-1999, 6, 6));
5732         }
5733 
5734         {
5735             auto date = Date(-1999, 7, 6);
5736             date.roll!"months"(6, AllowDayOverflow.no);
5737             assert(date == Date(-1999, 1, 6));
5738             date.roll!"months"(-6, AllowDayOverflow.no);
5739             assert(date == Date(-1999, 7, 6));
5740         }
5741 
5742         {
5743             auto date = Date(-1999, 7, 6);
5744             date.roll!"months"(-27, AllowDayOverflow.no);
5745             assert(date == Date(-1999, 4, 6));
5746             date.roll!"months"(28, AllowDayOverflow.no);
5747             assert(date == Date(-1999, 8, 6));
5748         }
5749 
5750         {
5751             auto date = Date(-1999, 5, 31);
5752             date.roll!"months"(1, AllowDayOverflow.no);
5753             assert(date == Date(-1999, 6, 30));
5754         }
5755 
5756         {
5757             auto date = Date(-1999, 5, 31);
5758             date.roll!"months"(-1, AllowDayOverflow.no);
5759             assert(date == Date(-1999, 4, 30));
5760         }
5761 
5762         {
5763             auto date = Date(-1999, 2, 28);
5764             date.roll!"months"(-12, AllowDayOverflow.no);
5765             assert(date == Date(-1999, 2, 28));
5766         }
5767 
5768         {
5769             auto date = Date(-2000, 2, 29);
5770             date.roll!"months"(-12, AllowDayOverflow.no);
5771             assert(date == Date(-2000, 2, 29));
5772         }
5773 
5774         {
5775             auto date = Date(-1999, 7, 31);
5776             date.roll!"months"(1, AllowDayOverflow.no);
5777             assert(date == Date(-1999, 8, 31));
5778             date.roll!"months"(1, AllowDayOverflow.no);
5779             assert(date == Date(-1999, 9, 30));
5780         }
5781 
5782         {
5783             auto date = Date(-1998, 8, 31);
5784             date.roll!"months"(13, AllowDayOverflow.no);
5785             assert(date == Date(-1998, 9, 30));
5786             date.roll!"months"(-13, AllowDayOverflow.no);
5787             assert(date == Date(-1998, 8, 30));
5788         }
5789 
5790         {
5791             auto date = Date(-1997, 12, 31);
5792             date.roll!"months"(13, AllowDayOverflow.no);
5793             assert(date == Date(-1997, 1, 31));
5794             date.roll!"months"(-13, AllowDayOverflow.no);
5795             assert(date == Date(-1997, 12, 31));
5796         }
5797 
5798         {
5799             auto date = Date(-1997, 12, 31);
5800             date.roll!"months"(14, AllowDayOverflow.no);
5801             assert(date == Date(-1997, 2, 28));
5802             date.roll!"months"(-14, AllowDayOverflow.no);
5803             assert(date == Date(-1997, 12, 28));
5804         }
5805 
5806         {
5807             auto date = Date(-2002, 12, 31);
5808             date.roll!"months"(14, AllowDayOverflow.no);
5809             assert(date == Date(-2002, 2, 28));
5810             date.roll!"months"(-14, AllowDayOverflow.no);
5811             assert(date == Date(-2002, 12, 28));
5812         }
5813 
5814         {
5815             auto date = Date(-2001, 12, 31);
5816             date.roll!"months"(14, AllowDayOverflow.no);
5817             assert(date == Date(-2001, 2, 28));
5818             date.roll!"months"(-14, AllowDayOverflow.no);
5819             assert(date == Date(-2001, 12, 28));
5820         }
5821 
5822         // Test Both
5823         {
5824             auto date = Date(1, 1, 1);
5825             date.roll!"months"(-1, AllowDayOverflow.no);
5826             assert(date == Date(1, 12, 1));
5827             date.roll!"months"(1, AllowDayOverflow.no);
5828             assert(date == Date(1, 1, 1));
5829         }
5830 
5831         {
5832             auto date = Date(4, 1, 1);
5833             date.roll!"months"(-48, AllowDayOverflow.no);
5834             assert(date == Date(4, 1, 1));
5835             date.roll!"months"(48, AllowDayOverflow.no);
5836             assert(date == Date(4, 1, 1));
5837         }
5838 
5839         {
5840             auto date = Date(4, 3, 31);
5841             date.roll!"months"(-49, AllowDayOverflow.no);
5842             assert(date == Date(4, 2, 29));
5843             date.roll!"months"(49, AllowDayOverflow.no);
5844             assert(date == Date(4, 3, 29));
5845         }
5846 
5847         {
5848             auto date = Date(4, 3, 31);
5849             date.roll!"months"(-85, AllowDayOverflow.no);
5850             assert(date == Date(4, 2, 29));
5851             date.roll!"months"(85, AllowDayOverflow.no);
5852             assert(date == Date(4, 3, 29));
5853         }
5854 
5855         {
5856             auto date = Date(-1, 1, 1);
5857             date.roll!"months"(-1, AllowDayOverflow.no);
5858             assert(date == Date(-1, 12, 1));
5859             date.roll!"months"(1, AllowDayOverflow.no);
5860             assert(date == Date(-1, 1, 1));
5861         }
5862 
5863         {
5864             auto date = Date(-4, 1, 1);
5865             date.roll!"months"(-48, AllowDayOverflow.no);
5866             assert(date == Date(-4, 1, 1));
5867             date.roll!"months"(48, AllowDayOverflow.no);
5868             assert(date == Date(-4, 1, 1));
5869         }
5870 
5871         {
5872             auto date = Date(-4, 3, 31);
5873             date.roll!"months"(-49, AllowDayOverflow.no);
5874             assert(date == Date(-4, 2, 29));
5875             date.roll!"months"(49, AllowDayOverflow.no);
5876             assert(date == Date(-4, 3, 29));
5877         }
5878 
5879         {
5880             auto date = Date(-4, 3, 31);
5881             date.roll!"months"(-85, AllowDayOverflow.no);
5882             assert(date == Date(-4, 2, 29));
5883             date.roll!"months"(85, AllowDayOverflow.no);
5884             assert(date == Date(-4, 3, 29));
5885         }
5886 
5887         {
5888             auto date = Date(-3, 3, 31);
5889             date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
5890             assert(date == Date(-3, 5, 30));
5891         }
5892     }
5893 
5894 
5895     /++
5896         Adds the given number of units to this $(LREF Date), mutating it. A
5897         negative number will subtract.
5898 
5899         The difference between rolling and adding is that rolling does not
5900         affect larger units. For instance, rolling a $(LREF Date) one
5901         year's worth of days gets the exact same $(LREF Date).
5902 
5903         The only accepted units are `"days"`.
5904 
5905         Params:
5906             units = The units to add. Must be `"days"`.
5907             days  = The number of days to add to this $(LREF Date).
5908 
5909         Returns:
5910             A reference to the `Date` (`this`).
5911       +/
5912     ref Date roll(string units)(long days) @safe pure nothrow @nogc
5913         if (units == "days")
5914     {
5915         immutable limit = maxDay(_year, _month);
5916         days %= limit;
5917         auto newDay = _day + days;
5918 
5919         if (days < 0)
5920         {
5921             if (newDay < 1)
5922                 newDay += limit;
5923         }
5924         else if (newDay > limit)
5925             newDay -= limit;
5926 
5927         _day = cast(ubyte) newDay;
5928         return this;
5929     }
5930 
5931     ///
5932     @safe unittest
5933     {
5934         auto d = Date(2010, 1, 1);
5935         d.roll!"days"(1);
5936         assert(d == Date(2010, 1, 2));
5937         d.roll!"days"(365);
5938         assert(d == Date(2010, 1, 26));
5939         d.roll!"days"(-32);
5940         assert(d == Date(2010, 1, 25));
5941     }
5942 
5943     @safe unittest
5944     {
5945         // Test A.D.
5946         {
5947             auto date = Date(1999, 2, 28);
5948             date.roll!"days"(1);
5949             assert(date == Date(1999, 2, 1));
5950             date.roll!"days"(-1);
5951             assert(date == Date(1999, 2, 28));
5952         }
5953 
5954         {
5955             auto date = Date(2000, 2, 28);
5956             date.roll!"days"(1);
5957             assert(date == Date(2000, 2, 29));
5958             date.roll!"days"(1);
5959             assert(date == Date(2000, 2, 1));
5960             date.roll!"days"(-1);
5961             assert(date == Date(2000, 2, 29));
5962         }
5963 
5964         {
5965             auto date = Date(1999, 6, 30);
5966             date.roll!"days"(1);
5967             assert(date == Date(1999, 6, 1));
5968             date.roll!"days"(-1);
5969             assert(date == Date(1999, 6, 30));
5970         }
5971 
5972         {
5973             auto date = Date(1999, 7, 31);
5974             date.roll!"days"(1);
5975             assert(date == Date(1999, 7, 1));
5976             date.roll!"days"(-1);
5977             assert(date == Date(1999, 7, 31));
5978         }
5979 
5980         {
5981             auto date = Date(1999, 1, 1);
5982             date.roll!"days"(-1);
5983             assert(date == Date(1999, 1, 31));
5984             date.roll!"days"(1);
5985             assert(date == Date(1999, 1, 1));
5986         }
5987 
5988         {
5989             auto date = Date(1999, 7, 6);
5990             date.roll!"days"(9);
5991             assert(date == Date(1999, 7, 15));
5992             date.roll!"days"(-11);
5993             assert(date == Date(1999, 7, 4));
5994             date.roll!"days"(30);
5995             assert(date == Date(1999, 7, 3));
5996             date.roll!"days"(-3);
5997             assert(date == Date(1999, 7, 31));
5998         }
5999 
6000         {
6001             auto date = Date(1999, 7, 6);
6002             date.roll!"days"(365);
6003             assert(date == Date(1999, 7, 30));
6004             date.roll!"days"(-365);
6005             assert(date == Date(1999, 7, 6));
6006             date.roll!"days"(366);
6007             assert(date == Date(1999, 7, 31));
6008             date.roll!"days"(730);
6009             assert(date == Date(1999, 7, 17));
6010             date.roll!"days"(-1096);
6011             assert(date == Date(1999, 7, 6));
6012         }
6013 
6014         {
6015             auto date = Date(1999, 2, 6);
6016             date.roll!"days"(365);
6017             assert(date == Date(1999, 2, 7));
6018             date.roll!"days"(-365);
6019             assert(date == Date(1999, 2, 6));
6020             date.roll!"days"(366);
6021             assert(date == Date(1999, 2, 8));
6022             date.roll!"days"(730);
6023             assert(date == Date(1999, 2, 10));
6024             date.roll!"days"(-1096);
6025             assert(date == Date(1999, 2, 6));
6026         }
6027 
6028         // Test B.C.
6029         {
6030             auto date = Date(-1999, 2, 28);
6031             date.roll!"days"(1);
6032             assert(date == Date(-1999, 2, 1));
6033             date.roll!"days"(-1);
6034             assert(date == Date(-1999, 2, 28));
6035         }
6036 
6037         {
6038             auto date = Date(-2000, 2, 28);
6039             date.roll!"days"(1);
6040             assert(date == Date(-2000, 2, 29));
6041             date.roll!"days"(1);
6042             assert(date == Date(-2000, 2, 1));
6043             date.roll!"days"(-1);
6044             assert(date == Date(-2000, 2, 29));
6045         }
6046 
6047         {
6048             auto date = Date(-1999, 6, 30);
6049             date.roll!"days"(1);
6050             assert(date == Date(-1999, 6, 1));
6051             date.roll!"days"(-1);
6052             assert(date == Date(-1999, 6, 30));
6053         }
6054 
6055         {
6056             auto date = Date(-1999, 7, 31);
6057             date.roll!"days"(1);
6058             assert(date == Date(-1999, 7, 1));
6059             date.roll!"days"(-1);
6060             assert(date == Date(-1999, 7, 31));
6061         }
6062 
6063         {
6064             auto date = Date(-1999, 1, 1);
6065             date.roll!"days"(-1);
6066             assert(date == Date(-1999, 1, 31));
6067             date.roll!"days"(1);
6068             assert(date == Date(-1999, 1, 1));
6069         }
6070 
6071         {
6072             auto date = Date(-1999, 7, 6);
6073             date.roll!"days"(9);
6074             assert(date == Date(-1999, 7, 15));
6075             date.roll!"days"(-11);
6076             assert(date == Date(-1999, 7, 4));
6077             date.roll!"days"(30);
6078             assert(date == Date(-1999, 7, 3));
6079             date.roll!"days"(-3);
6080             assert(date == Date(-1999, 7, 31));
6081         }
6082 
6083         {
6084             auto date = Date(-1999, 7, 6);
6085             date.roll!"days"(365);
6086             assert(date == Date(-1999, 7, 30));
6087             date.roll!"days"(-365);
6088             assert(date == Date(-1999, 7, 6));
6089             date.roll!"days"(366);
6090             assert(date == Date(-1999, 7, 31));
6091             date.roll!"days"(730);
6092             assert(date == Date(-1999, 7, 17));
6093             date.roll!"days"(-1096);
6094             assert(date == Date(-1999, 7, 6));
6095         }
6096 
6097         // Test Both
6098         {
6099             auto date = Date(1, 7, 6);
6100             date.roll!"days"(-365);
6101             assert(date == Date(1, 7, 13));
6102             date.roll!"days"(365);
6103             assert(date == Date(1, 7, 6));
6104             date.roll!"days"(-731);
6105             assert(date == Date(1, 7, 19));
6106             date.roll!"days"(730);
6107             assert(date == Date(1, 7, 5));
6108         }
6109 
6110         {
6111             auto date = Date(0, 7, 6);
6112             date.roll!"days"(-365);
6113             assert(date == Date(0, 7, 13));
6114             date.roll!"days"(365);
6115             assert(date == Date(0, 7, 6));
6116             date.roll!"days"(-731);
6117             assert(date == Date(0, 7, 19));
6118             date.roll!"days"(730);
6119             assert(date == Date(0, 7, 5));
6120         }
6121 
6122         {
6123             auto date = Date(0, 7, 6);
6124             date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
6125             assert(date == Date(0, 7, 8));
6126         }
6127 
6128         const cdate = Date(1999, 7, 6);
6129         immutable idate = Date(1999, 7, 6);
6130         static assert(!__traits(compiles, cdate.roll!"days"(12)));
6131         static assert(!__traits(compiles, idate.roll!"days"(12)));
6132     }
6133 
6134     import core.time : Duration;
6135     /++
6136         Gives the result of adding or subtracting a $(REF Duration, core,time)
6137         from
6138 
6139         The legal types of arithmetic for $(LREF Date) using this operator are
6140 
6141         $(BOOKTABLE,
6142         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6143         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6144         )
6145 
6146         Params:
6147             duration = The $(REF Duration, core,time) to add to or subtract from
6148                        this $(LREF Date).
6149       +/
6150     Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
6151         if (op == "+" || op == "-")
6152     {
6153         Date retval = this;
6154         immutable days = duration.total!"days";
6155         mixin("return retval._addDays(" ~ op ~ "days);");
6156     }
6157 
6158     ///
6159     @safe unittest
6160     {
6161         import core.time : days;
6162 
6163         assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
6164         assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
6165 
6166         assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
6167         assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
6168     }
6169 
6170     @safe unittest
6171     {
6172         auto date = Date(1999, 7, 6);
6173 
6174         import core.time : dur;
6175         assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
6176         assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
6177         assert(date + dur!"days"(7) == Date(1999, 7, 13));
6178         assert(date + dur!"days"(-7) == Date(1999, 6, 29));
6179 
6180         assert(date + dur!"hours"(24) == Date(1999, 7, 7));
6181         assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
6182         assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
6183         assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
6184         assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
6185         assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6186         assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6187         assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6188         assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6189         assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6190         assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6191         assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6192 
6193         assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
6194         assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
6195         assert(date - dur!"days"(-7) == Date(1999, 7, 13));
6196         assert(date - dur!"days"(7) == Date(1999, 6, 29));
6197 
6198         assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
6199         assert(date - dur!"hours"(24) == Date(1999, 7, 5));
6200         assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
6201         assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
6202         assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6203         assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
6204         assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6205         assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6206         assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6207         assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6208         assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6209         assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6210 
6211         auto duration = dur!"days"(12);
6212         const cdate = Date(1999, 7, 6);
6213         immutable idate = Date(1999, 7, 6);
6214         assert(date + duration == Date(1999, 7, 18));
6215         assert(cdate + duration == Date(1999, 7, 18));
6216         assert(idate + duration == Date(1999, 7, 18));
6217 
6218         assert(date - duration == Date(1999, 6, 24));
6219         assert(cdate - duration == Date(1999, 6, 24));
6220         assert(idate - duration == Date(1999, 6, 24));
6221     }
6222 
6223 
6224     /++
6225         Gives the result of adding or subtracting a $(REF Duration, core,time)
6226         from this $(LREF Date), as well as assigning the result to this
6227         $(LREF Date).
6228 
6229         The legal types of arithmetic for $(LREF Date) using this operator are
6230 
6231         $(BOOKTABLE,
6232         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6233         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6234         )
6235 
6236         Params:
6237             duration = The $(REF Duration, core,time) to add to or subtract from
6238                        this $(LREF Date).
6239       +/
6240     ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
6241         if (op == "+" || op == "-")
6242     {
6243         immutable days = duration.total!"days";
6244         mixin("return _addDays(" ~ op ~ "days);");
6245     }
6246 
6247     @safe unittest
6248     {
6249         import core.time : dur;
6250         assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
6251         assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
6252         assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
6253         assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
6254 
6255         assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
6256         assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
6257         assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
6258         assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
6259         assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
6260         assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6261         assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6262         assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6263         assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6264         assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6265         assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6266         assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6267 
6268         assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
6269         assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
6270         assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
6271         assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
6272 
6273         assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
6274         assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
6275         assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
6276         assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
6277         assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6278         assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
6279         assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6280         assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6281         assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6282         assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6283         assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6284         assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6285 
6286         {
6287             auto date = Date(0, 1, 31);
6288             (date += dur!"days"(507)) += dur!"days"(-2);
6289             assert(date == Date(1, 6, 19));
6290         }
6291 
6292         auto duration = dur!"days"(12);
6293         auto date = Date(1999, 7, 6);
6294         const cdate = Date(1999, 7, 6);
6295         immutable idate = Date(1999, 7, 6);
6296         date += duration;
6297         static assert(!__traits(compiles, cdate += duration));
6298         static assert(!__traits(compiles, idate += duration));
6299 
6300         date -= duration;
6301         static assert(!__traits(compiles, cdate -= duration));
6302         static assert(!__traits(compiles, idate -= duration));
6303     }
6304 
6305 
6306     /++
6307         Gives the difference between two $(LREF Date)s.
6308 
6309         The legal types of arithmetic for $(LREF Date) using this operator are
6310 
6311         $(BOOKTABLE,
6312         $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
6313         )
6314       +/
6315     Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
6316         if (op == "-")
6317     {
6318         import core.time : dur;
6319         return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
6320     }
6321 
6322     @safe unittest
6323     {
6324         auto date = Date(1999, 7, 6);
6325 
6326         import core.time : dur;
6327         assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
6328         assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
6329         assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
6330         assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
6331         assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
6332         assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
6333 
6334         const cdate = Date(1999, 7, 6);
6335         immutable idate = Date(1999, 7, 6);
6336         assert(date - date == Duration.zero);
6337         assert(cdate - date == Duration.zero);
6338         assert(idate - date == Duration.zero);
6339 
6340         assert(date - cdate == Duration.zero);
6341         assert(cdate - cdate == Duration.zero);
6342         assert(idate - cdate == Duration.zero);
6343 
6344         assert(date - idate == Duration.zero);
6345         assert(cdate - idate == Duration.zero);
6346         assert(idate - idate == Duration.zero);
6347     }
6348 
6349 
6350     /++
6351         Returns the difference between the two $(LREF Date)s in months.
6352 
6353         To get the difference in years, subtract the year property
6354         of two $(LREF Date)s. To get the difference in days or weeks,
6355         subtract the $(LREF Date)s themselves and use the
6356         $(REF Duration, core,time) that results. Because converting between
6357         months and smaller units requires a specific date (which
6358         $(REF Duration, core,time)s don't have), getting the difference in
6359         months requires some math using both the year and month properties, so
6360         this is a convenience function for getting the difference in months.
6361 
6362         Note that the number of days in the months or how far into the month
6363         either $(LREF Date) is is irrelevant. It is the difference in the month
6364         property combined with the difference in years * 12. So, for instance,
6365         December 31st and January 1st are one month apart just as December 1st
6366         and January 31st are one month apart.
6367 
6368         Params:
6369             rhs = The $(LREF Date) to subtract from this one.
6370       +/
6371     int diffMonths(Date rhs) const @safe pure nothrow @nogc
6372     {
6373         immutable yearDiff = _year - rhs._year;
6374         immutable monthDiff = _month - rhs._month;
6375 
6376         return yearDiff * 12 + monthDiff;
6377     }
6378 
6379     ///
6380     @safe unittest
6381     {
6382         assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
6383         assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
6384         assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
6385         assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
6386     }
6387 
6388     @safe unittest
6389     {
6390         auto date = Date(1999, 7, 6);
6391 
6392         // Test A.D.
6393         assert(date.diffMonths(Date(1998, 6, 5)) == 13);
6394         assert(date.diffMonths(Date(1998, 7, 5)) == 12);
6395         assert(date.diffMonths(Date(1998, 8, 5)) == 11);
6396         assert(date.diffMonths(Date(1998, 9, 5)) == 10);
6397         assert(date.diffMonths(Date(1998, 10, 5)) == 9);
6398         assert(date.diffMonths(Date(1998, 11, 5)) == 8);
6399         assert(date.diffMonths(Date(1998, 12, 5)) == 7);
6400         assert(date.diffMonths(Date(1999, 1, 5)) == 6);
6401         assert(date.diffMonths(Date(1999, 2, 6)) == 5);
6402         assert(date.diffMonths(Date(1999, 3, 6)) == 4);
6403         assert(date.diffMonths(Date(1999, 4, 6)) == 3);
6404         assert(date.diffMonths(Date(1999, 5, 6)) == 2);
6405         assert(date.diffMonths(Date(1999, 6, 6)) == 1);
6406         assert(date.diffMonths(date) == 0);
6407         assert(date.diffMonths(Date(1999, 8, 6)) == -1);
6408         assert(date.diffMonths(Date(1999, 9, 6)) == -2);
6409         assert(date.diffMonths(Date(1999, 10, 6)) == -3);
6410         assert(date.diffMonths(Date(1999, 11, 6)) == -4);
6411         assert(date.diffMonths(Date(1999, 12, 6)) == -5);
6412         assert(date.diffMonths(Date(2000, 1, 6)) == -6);
6413         assert(date.diffMonths(Date(2000, 2, 6)) == -7);
6414         assert(date.diffMonths(Date(2000, 3, 6)) == -8);
6415         assert(date.diffMonths(Date(2000, 4, 6)) == -9);
6416         assert(date.diffMonths(Date(2000, 5, 6)) == -10);
6417         assert(date.diffMonths(Date(2000, 6, 6)) == -11);
6418         assert(date.diffMonths(Date(2000, 7, 6)) == -12);
6419         assert(date.diffMonths(Date(2000, 8, 6)) == -13);
6420 
6421         assert(Date(1998, 6, 5).diffMonths(date) == -13);
6422         assert(Date(1998, 7, 5).diffMonths(date) == -12);
6423         assert(Date(1998, 8, 5).diffMonths(date) == -11);
6424         assert(Date(1998, 9, 5).diffMonths(date) == -10);
6425         assert(Date(1998, 10, 5).diffMonths(date) == -9);
6426         assert(Date(1998, 11, 5).diffMonths(date) == -8);
6427         assert(Date(1998, 12, 5).diffMonths(date) == -7);
6428         assert(Date(1999, 1, 5).diffMonths(date) == -6);
6429         assert(Date(1999, 2, 6).diffMonths(date) == -5);
6430         assert(Date(1999, 3, 6).diffMonths(date) == -4);
6431         assert(Date(1999, 4, 6).diffMonths(date) == -3);
6432         assert(Date(1999, 5, 6).diffMonths(date) == -2);
6433         assert(Date(1999, 6, 6).diffMonths(date) == -1);
6434         assert(Date(1999, 8, 6).diffMonths(date) == 1);
6435         assert(Date(1999, 9, 6).diffMonths(date) == 2);
6436         assert(Date(1999, 10, 6).diffMonths(date) == 3);
6437         assert(Date(1999, 11, 6).diffMonths(date) == 4);
6438         assert(Date(1999, 12, 6).diffMonths(date) == 5);
6439         assert(Date(2000, 1, 6).diffMonths(date) == 6);
6440         assert(Date(2000, 2, 6).diffMonths(date) == 7);
6441         assert(Date(2000, 3, 6).diffMonths(date) == 8);
6442         assert(Date(2000, 4, 6).diffMonths(date) == 9);
6443         assert(Date(2000, 5, 6).diffMonths(date) == 10);
6444         assert(Date(2000, 6, 6).diffMonths(date) == 11);
6445         assert(Date(2000, 7, 6).diffMonths(date) == 12);
6446         assert(Date(2000, 8, 6).diffMonths(date) == 13);
6447 
6448         assert(date.diffMonths(Date(1999, 6, 30)) == 1);
6449         assert(date.diffMonths(Date(1999, 7, 1)) == 0);
6450         assert(date.diffMonths(Date(1999, 7, 6)) == 0);
6451         assert(date.diffMonths(Date(1999, 7, 11)) == 0);
6452         assert(date.diffMonths(Date(1999, 7, 16)) == 0);
6453         assert(date.diffMonths(Date(1999, 7, 21)) == 0);
6454         assert(date.diffMonths(Date(1999, 7, 26)) == 0);
6455         assert(date.diffMonths(Date(1999, 7, 31)) == 0);
6456         assert(date.diffMonths(Date(1999, 8, 1)) == -1);
6457 
6458         assert(date.diffMonths(Date(1990, 6, 30)) == 109);
6459         assert(date.diffMonths(Date(1990, 7, 1)) == 108);
6460         assert(date.diffMonths(Date(1990, 7, 6)) == 108);
6461         assert(date.diffMonths(Date(1990, 7, 11)) == 108);
6462         assert(date.diffMonths(Date(1990, 7, 16)) == 108);
6463         assert(date.diffMonths(Date(1990, 7, 21)) == 108);
6464         assert(date.diffMonths(Date(1990, 7, 26)) == 108);
6465         assert(date.diffMonths(Date(1990, 7, 31)) == 108);
6466         assert(date.diffMonths(Date(1990, 8, 1)) == 107);
6467 
6468         assert(Date(1999, 6, 30).diffMonths(date) == -1);
6469         assert(Date(1999, 7, 1).diffMonths(date) == 0);
6470         assert(Date(1999, 7, 6).diffMonths(date) == 0);
6471         assert(Date(1999, 7, 11).diffMonths(date) == 0);
6472         assert(Date(1999, 7, 16).diffMonths(date) == 0);
6473         assert(Date(1999, 7, 21).diffMonths(date) == 0);
6474         assert(Date(1999, 7, 26).diffMonths(date) == 0);
6475         assert(Date(1999, 7, 31).diffMonths(date) == 0);
6476         assert(Date(1999, 8, 1).diffMonths(date) == 1);
6477 
6478         assert(Date(1990, 6, 30).diffMonths(date) == -109);
6479         assert(Date(1990, 7, 1).diffMonths(date) == -108);
6480         assert(Date(1990, 7, 6).diffMonths(date) == -108);
6481         assert(Date(1990, 7, 11).diffMonths(date) == -108);
6482         assert(Date(1990, 7, 16).diffMonths(date) == -108);
6483         assert(Date(1990, 7, 21).diffMonths(date) == -108);
6484         assert(Date(1990, 7, 26).diffMonths(date) == -108);
6485         assert(Date(1990, 7, 31).diffMonths(date) == -108);
6486         assert(Date(1990, 8, 1).diffMonths(date) == -107);
6487 
6488         // Test B.C.
6489         auto dateBC = Date(-1999, 7, 6);
6490 
6491         assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
6492         assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
6493         assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
6494         assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
6495         assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
6496         assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
6497         assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
6498         assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
6499         assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
6500         assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
6501         assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
6502         assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
6503         assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
6504         assert(dateBC.diffMonths(dateBC) == 0);
6505         assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
6506         assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
6507         assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
6508         assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
6509         assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
6510         assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
6511         assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
6512         assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
6513         assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
6514         assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
6515         assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
6516         assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
6517         assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
6518 
6519         assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
6520         assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
6521         assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
6522         assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
6523         assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
6524         assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
6525         assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
6526         assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
6527         assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
6528         assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
6529         assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
6530         assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
6531         assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
6532         assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
6533         assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
6534         assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
6535         assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
6536         assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
6537         assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
6538         assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
6539         assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
6540         assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
6541         assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
6542         assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
6543         assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
6544         assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
6545 
6546         assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
6547         assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
6548         assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
6549         assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
6550         assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
6551         assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
6552         assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
6553         assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
6554         assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
6555 
6556         assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
6557         assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
6558         assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
6559         assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
6560         assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
6561         assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
6562         assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
6563         assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
6564         assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
6565 
6566         assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
6567         assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
6568         assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
6569         assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
6570         assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
6571         assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
6572         assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
6573         assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
6574         assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
6575 
6576         assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
6577         assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
6578         assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
6579         assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
6580         assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
6581         assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
6582         assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
6583         assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
6584         assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
6585 
6586         // Test Both
6587         assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
6588         assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
6589 
6590         const cdate = Date(1999, 7, 6);
6591         immutable idate = Date(1999, 7, 6);
6592         assert(date.diffMonths(date) == 0);
6593         assert(cdate.diffMonths(date) == 0);
6594         assert(idate.diffMonths(date) == 0);
6595 
6596         assert(date.diffMonths(cdate) == 0);
6597         assert(cdate.diffMonths(cdate) == 0);
6598         assert(idate.diffMonths(cdate) == 0);
6599 
6600         assert(date.diffMonths(idate) == 0);
6601         assert(cdate.diffMonths(idate) == 0);
6602         assert(idate.diffMonths(idate) == 0);
6603     }
6604 
6605 
6606     /++
6607         Whether this $(LREF Date) is in a leap year.
6608      +/
6609     @property bool isLeapYear() const @safe pure nothrow @nogc
6610     {
6611         return yearIsLeapYear(_year);
6612     }
6613 
6614     @safe unittest
6615     {
6616         auto date = Date(1999, 7, 6);
6617         const cdate = Date(1999, 7, 6);
6618         immutable idate = Date(1999, 7, 6);
6619         static assert(!__traits(compiles, date.isLeapYear = true));
6620         static assert(!__traits(compiles, cdate.isLeapYear = true));
6621         static assert(!__traits(compiles, idate.isLeapYear = true));
6622     }
6623 
6624 
6625     /++
6626         Day of the week this $(LREF Date) is on.
6627       +/
6628     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
6629     {
6630         return getDayOfWeek(dayOfGregorianCal);
6631     }
6632 
6633     @safe unittest
6634     {
6635         const cdate = Date(1999, 7, 6);
6636         immutable idate = Date(1999, 7, 6);
6637         assert(cdate.dayOfWeek == DayOfWeek.tue);
6638         static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
6639         assert(idate.dayOfWeek == DayOfWeek.tue);
6640         static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
6641     }
6642 
6643 
6644     /++
6645         Day of the year this $(LREF Date) is on.
6646       +/
6647     @property ushort dayOfYear() const @safe pure nothrow @nogc
6648     {
6649         if (_month >= Month.jan && _month <= Month.dec)
6650         {
6651             immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6652             auto monthIndex = _month - Month.jan;
6653 
6654             return cast(ushort)(lastDay[monthIndex] + _day);
6655         }
6656         assert(0, "Invalid month.");
6657     }
6658 
6659     ///
6660     @safe unittest
6661     {
6662         assert(Date(1999, 1, 1).dayOfYear == 1);
6663         assert(Date(1999, 12, 31).dayOfYear == 365);
6664         assert(Date(2000, 12, 31).dayOfYear == 366);
6665     }
6666 
6667     @safe unittest
6668     {
6669         import std.algorithm.iteration : filter;
6670         import std.range : chain;
6671 
6672         foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6673         {
6674             foreach (doy; testDaysOfYear)
6675                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6676         }
6677 
6678         foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6679         {
6680             foreach (doy; testDaysOfLeapYear)
6681                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6682         }
6683 
6684         const cdate = Date(1999, 7, 6);
6685         immutable idate = Date(1999, 7, 6);
6686         assert(cdate.dayOfYear == 187);
6687         assert(idate.dayOfYear == 187);
6688     }
6689 
6690     /++
6691         Day of the year.
6692 
6693         Params:
6694             day = The day of the year to set which day of the year this
6695                   $(LREF Date) is on.
6696 
6697         Throws:
6698             $(REF DateTimeException,std,datetime,date) if the given day is an
6699             invalid day of the year.
6700       +/
6701     @property void dayOfYear(int day) @safe pure
6702     {
6703         setDayOfYear!true(day);
6704     }
6705 
6706     private void setDayOfYear(bool useExceptions = false)(int day)
6707     {
6708         immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6709 
6710         bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
6711         enum errorMsg = "Invalid day of the year.";
6712 
6713         static if (useExceptions)
6714         {
6715             if (dayOutOfRange) throw new DateTimeException(errorMsg);
6716         }
6717         else
6718         {
6719             assert(!dayOutOfRange, errorMsg);
6720         }
6721 
6722         foreach (i; 1 .. lastDay.length)
6723         {
6724             if (day <= lastDay[i])
6725             {
6726                 _month = cast(Month)(cast(int) Month.jan + i - 1);
6727                 _day = cast(ubyte)(day - lastDay[i - 1]);
6728                 return;
6729             }
6730         }
6731         assert(0, "Invalid day of the year.");
6732     }
6733 
6734     @safe unittest
6735     {
6736         static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
6737         {
6738             date.dayOfYear = day;
6739             assert(date.month == expected.month);
6740             assert(date.day == expected.day);
6741         }
6742 
6743         foreach (doy; testDaysOfYear)
6744         {
6745             test(Date(1999, 1, 1), doy.day, doy.md);
6746             test(Date(-1, 1, 1), doy.day, doy.md);
6747         }
6748 
6749         foreach (doy; testDaysOfLeapYear)
6750         {
6751             test(Date(2000, 1, 1), doy.day, doy.md);
6752             test(Date(-4, 1, 1), doy.day, doy.md);
6753         }
6754 
6755         const cdate = Date(1999, 7, 6);
6756         immutable idate = Date(1999, 7, 6);
6757         static assert(!__traits(compiles, cdate.dayOfYear = 187));
6758         static assert(!__traits(compiles, idate.dayOfYear = 187));
6759     }
6760 
6761 
6762     /++
6763         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6764      +/
6765     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
6766     {
6767         if (isAD)
6768         {
6769             if (_year == 1)
6770                 return dayOfYear;
6771 
6772             int years = _year - 1;
6773             auto days = (years / 400) * daysIn400Years;
6774             years %= 400;
6775 
6776             days += (years / 100) * daysIn100Years;
6777             years %= 100;
6778 
6779             days += (years / 4) * daysIn4Years;
6780             years %= 4;
6781 
6782             days += years * daysInYear;
6783 
6784             days += dayOfYear;
6785 
6786             return days;
6787         }
6788         else if (_year == 0)
6789             return dayOfYear - daysInLeapYear;
6790         else
6791         {
6792             int years = _year;
6793             auto days = (years / 400) * daysIn400Years;
6794             years %= 400;
6795 
6796             days += (years / 100) * daysIn100Years;
6797             years %= 100;
6798 
6799             days += (years / 4) * daysIn4Years;
6800             years %= 4;
6801 
6802             if (years < 0)
6803             {
6804                 days -= daysInLeapYear;
6805                 ++years;
6806 
6807                 days += years * daysInYear;
6808 
6809                 days -= daysInYear - dayOfYear;
6810             }
6811             else
6812                 days -= daysInLeapYear - dayOfYear;
6813 
6814             return days;
6815         }
6816     }
6817 
6818     ///
6819     @safe unittest
6820     {
6821         assert(Date(1, 1, 1).dayOfGregorianCal == 1);
6822         assert(Date(1, 12, 31).dayOfGregorianCal == 365);
6823         assert(Date(2, 1, 1).dayOfGregorianCal == 366);
6824 
6825         assert(Date(0, 12, 31).dayOfGregorianCal == 0);
6826         assert(Date(0, 1, 1).dayOfGregorianCal == -365);
6827         assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
6828 
6829         assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
6830         assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
6831     }
6832 
6833     @safe unittest
6834     {
6835         import std.range : chain;
6836 
6837         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
6838             assert(gd.date.dayOfGregorianCal == gd.day);
6839 
6840         auto date = Date(1999, 7, 6);
6841         const cdate = Date(1999, 7, 6);
6842         immutable idate = Date(1999, 7, 6);
6843         assert(date.dayOfGregorianCal == 729_941);
6844         assert(cdate.dayOfGregorianCal == 729_941);
6845         assert(idate.dayOfGregorianCal == 729_941);
6846     }
6847 
6848     /++
6849         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6850 
6851         Params:
6852             day = The day of the Gregorian Calendar to set this $(LREF Date) to.
6853      +/
6854     @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
6855     {
6856         this = Date(day);
6857     }
6858 
6859     ///
6860     @safe unittest
6861     {
6862         auto date = Date.init;
6863         date.dayOfGregorianCal = 1;
6864         assert(date == Date(1, 1, 1));
6865 
6866         date.dayOfGregorianCal = 365;
6867         assert(date == Date(1, 12, 31));
6868 
6869         date.dayOfGregorianCal = 366;
6870         assert(date == Date(2, 1, 1));
6871 
6872         date.dayOfGregorianCal = 0;
6873         assert(date == Date(0, 12, 31));
6874 
6875         date.dayOfGregorianCal = -365;
6876         assert(date == Date(-0, 1, 1));
6877 
6878         date.dayOfGregorianCal = -366;
6879         assert(date == Date(-1, 12, 31));
6880 
6881         date.dayOfGregorianCal = 730_120;
6882         assert(date == Date(2000, 1, 1));
6883 
6884         date.dayOfGregorianCal = 734_137;
6885         assert(date == Date(2010, 12, 31));
6886     }
6887 
6888     @safe unittest
6889     {
6890         auto date = Date(1999, 7, 6);
6891         const cdate = Date(1999, 7, 6);
6892         immutable idate = Date(1999, 7, 6);
6893         date.dayOfGregorianCal = 187;
6894         assert(date.dayOfGregorianCal == 187);
6895         static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
6896         static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
6897     }
6898 
6899 
6900     /++
6901         The ISO 8601 week and year of the year that this $(LREF Date) is in.
6902 
6903         Returns:
6904             An anonymous struct with the members $(D isoWeekYear) for the
6905             resulting year and $(D isoWeek) for the resulting ISO week.
6906 
6907         See_Also:
6908             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6909       +/
6910     @property auto isoWeekAndYear() const @safe pure nothrow
6911     {
6912         struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
6913 
6914         immutable weekday = dayOfWeek;
6915         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
6916         immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
6917 
6918         try
6919         {
6920             if (week == 53)
6921             {
6922                 switch (Date(_year + 1, 1, 1).dayOfWeek)
6923                 {
6924                     case DayOfWeek.mon:
6925                     case DayOfWeek.tue:
6926                     case DayOfWeek.wed:
6927                     case DayOfWeek.thu:
6928                         return ISOWeekAndYear(cast(short) (_year + 1), 1);
6929                     case DayOfWeek.fri:
6930                     case DayOfWeek.sat:
6931                     case DayOfWeek.sun:
6932                         return ISOWeekAndYear(_year, 53);
6933                     default:
6934                         assert(0, "Invalid ISO Week");
6935                 }
6936             }
6937             else if (week > 0)
6938                 return ISOWeekAndYear(_year, cast(ubyte) week);
6939             else
6940                 return Date(_year - 1, 12, 31).isoWeekAndYear;
6941         }
6942         catch (Exception e)
6943             assert(0, "Date's constructor threw.");
6944     }
6945 
6946     /++
6947         The ISO 8601 week of the year that this $(LREF Date) is in.
6948 
6949         See_Also:
6950             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6951       +/
6952     @property ubyte isoWeek() const @safe pure nothrow
6953     {
6954         return isoWeekAndYear().isoWeek;
6955     }
6956 
6957     @safe unittest
6958     {
6959         // Test A.D.
6960         assert(Date(2009, 12, 28).isoWeek == 53);
6961         assert(Date(2009, 12, 29).isoWeek == 53);
6962         assert(Date(2009, 12, 30).isoWeek == 53);
6963         assert(Date(2009, 12, 31).isoWeek == 53);
6964         assert(Date(2010, 1, 1).isoWeek == 53);
6965         assert(Date(2010, 1, 2).isoWeek == 53);
6966         assert(Date(2010, 1, 3).isoWeek == 53);
6967         assert(Date(2010, 1, 4).isoWeek == 1);
6968         assert(Date(2010, 1, 5).isoWeek == 1);
6969         assert(Date(2010, 1, 6).isoWeek == 1);
6970         assert(Date(2010, 1, 7).isoWeek == 1);
6971         assert(Date(2010, 1, 8).isoWeek == 1);
6972         assert(Date(2010, 1, 9).isoWeek == 1);
6973         assert(Date(2010, 1, 10).isoWeek == 1);
6974         assert(Date(2010, 1, 11).isoWeek == 2);
6975         assert(Date(2010, 12, 31).isoWeek == 52);
6976 
6977         assert(Date(2004, 12, 26).isoWeek == 52);
6978         assert(Date(2004, 12, 27).isoWeek == 53);
6979         assert(Date(2004, 12, 28).isoWeek == 53);
6980         assert(Date(2004, 12, 29).isoWeek == 53);
6981         assert(Date(2004, 12, 30).isoWeek == 53);
6982         assert(Date(2004, 12, 31).isoWeek == 53);
6983         assert(Date(2005, 1, 1).isoWeek == 53);
6984         assert(Date(2005, 1, 2).isoWeek == 53);
6985 
6986         assert(Date(2005, 12, 31).isoWeek == 52);
6987         assert(Date(2007, 1, 1).isoWeek == 1);
6988 
6989         assert(Date(2007, 12, 30).isoWeek == 52);
6990         assert(Date(2007, 12, 31).isoWeek == 1);
6991         assert(Date(2008, 1, 1).isoWeek == 1);
6992 
6993         assert(Date(2008, 12, 28).isoWeek == 52);
6994         assert(Date(2008, 12, 29).isoWeek == 1);
6995         assert(Date(2008, 12, 30).isoWeek == 1);
6996         assert(Date(2008, 12, 31).isoWeek == 1);
6997         assert(Date(2009, 1, 1).isoWeek == 1);
6998         assert(Date(2009, 1, 2).isoWeek == 1);
6999         assert(Date(2009, 1, 3).isoWeek == 1);
7000         assert(Date(2009, 1, 4).isoWeek == 1);
7001 
7002         // Test B.C.
7003         // The algorithm should work identically for both A.D. and B.C. since
7004         // it doesn't really take the year into account, so B.C. testing
7005         // probably isn't really needed.
7006         assert(Date(0, 12, 31).isoWeek == 52);
7007         assert(Date(0, 1, 4).isoWeek == 1);
7008         assert(Date(0, 1, 1).isoWeek == 52);
7009 
7010         const cdate = Date(1999, 7, 6);
7011         immutable idate = Date(1999, 7, 6);
7012         assert(cdate.isoWeek == 27);
7013         static assert(!__traits(compiles, cdate.isoWeek = 3));
7014         assert(idate.isoWeek == 27);
7015         static assert(!__traits(compiles, idate.isoWeek = 3));
7016     }
7017 
7018     /++
7019         The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
7020 
7021         May differ from $(LREF year) between 28 December and 4 January.
7022 
7023         See_Also:
7024             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
7025       +/
7026     @property short isoWeekYear() const @safe pure nothrow
7027     {
7028         return isoWeekAndYear().isoWeekYear;
7029     }
7030 
7031     @safe unittest
7032     {
7033         // Test A.D.
7034         assert(Date(2009, 12, 28).isoWeekYear == 2009);
7035         assert(Date(2009, 12, 29).isoWeekYear == 2009);
7036         assert(Date(2009, 12, 30).isoWeekYear == 2009);
7037         assert(Date(2009, 12, 31).isoWeekYear == 2009);
7038         assert(Date(2010, 1, 1).isoWeekYear == 2009);
7039         assert(Date(2010, 1, 2).isoWeekYear == 2009);
7040         assert(Date(2010, 1, 3).isoWeekYear == 2009);
7041         assert(Date(2010, 1, 4).isoWeekYear == 2010);
7042         assert(Date(2010, 1, 5).isoWeekYear == 2010);
7043         assert(Date(2010, 1, 6).isoWeekYear == 2010);
7044         assert(Date(2010, 1, 7).isoWeekYear == 2010);
7045         assert(Date(2010, 1, 8).isoWeekYear == 2010);
7046         assert(Date(2010, 1, 9).isoWeekYear == 2010);
7047         assert(Date(2010, 1, 10).isoWeekYear == 2010);
7048         assert(Date(2010, 1, 11).isoWeekYear == 2010);
7049         assert(Date(2010, 12, 31).isoWeekYear == 2010);
7050 
7051         assert(Date(2004, 12, 26).isoWeekYear == 2004);
7052         assert(Date(2004, 12, 27).isoWeekYear == 2004);
7053         assert(Date(2004, 12, 28).isoWeekYear == 2004);
7054         assert(Date(2004, 12, 29).isoWeekYear == 2004);
7055         assert(Date(2004, 12, 30).isoWeekYear == 2004);
7056         assert(Date(2004, 12, 31).isoWeekYear == 2004);
7057         assert(Date(2005, 1, 1).isoWeekYear == 2004);
7058         assert(Date(2005, 1, 2).isoWeekYear == 2004);
7059         assert(Date(2005, 1, 3).isoWeekYear == 2005);
7060 
7061         assert(Date(2005, 12, 31).isoWeekYear == 2005);
7062         assert(Date(2007, 1, 1).isoWeekYear == 2007);
7063 
7064         assert(Date(2007, 12, 30).isoWeekYear == 2007);
7065         assert(Date(2007, 12, 31).isoWeekYear == 2008);
7066         assert(Date(2008, 1, 1).isoWeekYear == 2008);
7067 
7068         assert(Date(2008, 12, 28).isoWeekYear == 2008);
7069         assert(Date(2008, 12, 29).isoWeekYear == 2009);
7070         assert(Date(2008, 12, 30).isoWeekYear == 2009);
7071         assert(Date(2008, 12, 31).isoWeekYear == 2009);
7072         assert(Date(2009, 1, 1).isoWeekYear == 2009);
7073         assert(Date(2009, 1, 2).isoWeekYear == 2009);
7074         assert(Date(2009, 1, 3).isoWeekYear == 2009);
7075         assert(Date(2009, 1, 4).isoWeekYear == 2009);
7076 
7077         // Test B.C.
7078         assert(Date(0, 12, 31).isoWeekYear == 0);
7079         assert(Date(0, 1, 4).isoWeekYear == 0);
7080         assert(Date(0, 1, 1).isoWeekYear == -1);
7081 
7082         const cdate = Date(1999, 7, 6);
7083         immutable idate = Date(1999, 7, 6);
7084         assert(cdate.isoWeekYear == 1999);
7085         assert(idate.isoWeekYear == 1999);
7086     }
7087 
7088     static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
7089     {
7090         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
7091         immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
7092 
7093         Date date;
7094         date._year = isoWeekYear;
7095         date._month = Month.jan;
7096         date._day = 3;
7097         immutable startOfYear = date.dayOfWeek;
7098         return date._addDays(dayOffset - startOfYear);
7099     }
7100 
7101     @safe unittest
7102     {
7103         // Test -30000 days to 30000 days for matching construction <-> deconstruction
7104         Date date = Date(1, 1, 1);
7105         date._addDays(-30_000);
7106         foreach (day; 0 .. 60_000)
7107         {
7108             const year = date.isoWeekYear;
7109             const dow = date.dayOfWeek;
7110             const isoWeek = date.isoWeek;
7111             const reversed = Date.fromISOWeek(year, isoWeek, dow);
7112             assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
7113             date = date._addDays(1);
7114         }
7115     }
7116 
7117 
7118     /++
7119         $(LREF Date) for the last day in the month that this $(LREF Date) is in.
7120       +/
7121     @property Date endOfMonth() const @safe pure nothrow
7122     {
7123         try
7124             return Date(_year, _month, maxDay(_year, _month));
7125         catch (Exception e)
7126             assert(0, "Date's constructor threw.");
7127     }
7128 
7129     ///
7130     @safe unittest
7131     {
7132         assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
7133         assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
7134         assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
7135         assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
7136     }
7137 
7138     @safe unittest
7139     {
7140         // Test A.D.
7141         assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
7142         assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
7143         assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
7144         assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
7145         assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
7146         assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
7147         assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
7148         assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
7149         assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
7150         assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
7151         assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
7152         assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
7153         assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
7154 
7155         // Test B.C.
7156         assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
7157         assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
7158         assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
7159         assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
7160         assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
7161         assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
7162         assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
7163         assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
7164         assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
7165         assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
7166         assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
7167         assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
7168         assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
7169 
7170         const cdate = Date(1999, 7, 6);
7171         immutable idate = Date(1999, 7, 6);
7172         static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
7173         static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
7174     }
7175 
7176 
7177     /++
7178         The last day in the month that this $(LREF Date) is in.
7179       +/
7180     @property ubyte daysInMonth() const @safe pure nothrow @nogc
7181     {
7182         return maxDay(_year, _month);
7183     }
7184 
7185     ///
7186     @safe unittest
7187     {
7188         assert(Date(1999, 1, 6).daysInMonth == 31);
7189         assert(Date(1999, 2, 7).daysInMonth == 28);
7190         assert(Date(2000, 2, 7).daysInMonth == 29);
7191         assert(Date(2000, 6, 4).daysInMonth == 30);
7192     }
7193 
7194     @safe unittest
7195     {
7196         // Test A.D.
7197         assert(Date(1999, 1, 1).daysInMonth == 31);
7198         assert(Date(1999, 2, 1).daysInMonth == 28);
7199         assert(Date(2000, 2, 1).daysInMonth == 29);
7200         assert(Date(1999, 3, 1).daysInMonth == 31);
7201         assert(Date(1999, 4, 1).daysInMonth == 30);
7202         assert(Date(1999, 5, 1).daysInMonth == 31);
7203         assert(Date(1999, 6, 1).daysInMonth == 30);
7204         assert(Date(1999, 7, 1).daysInMonth == 31);
7205         assert(Date(1999, 8, 1).daysInMonth == 31);
7206         assert(Date(1999, 9, 1).daysInMonth == 30);
7207         assert(Date(1999, 10, 1).daysInMonth == 31);
7208         assert(Date(1999, 11, 1).daysInMonth == 30);
7209         assert(Date(1999, 12, 1).daysInMonth == 31);
7210 
7211         // Test B.C.
7212         assert(Date(-1999, 1, 1).daysInMonth == 31);
7213         assert(Date(-1999, 2, 1).daysInMonth == 28);
7214         assert(Date(-2000, 2, 1).daysInMonth == 29);
7215         assert(Date(-1999, 3, 1).daysInMonth == 31);
7216         assert(Date(-1999, 4, 1).daysInMonth == 30);
7217         assert(Date(-1999, 5, 1).daysInMonth == 31);
7218         assert(Date(-1999, 6, 1).daysInMonth == 30);
7219         assert(Date(-1999, 7, 1).daysInMonth == 31);
7220         assert(Date(-1999, 8, 1).daysInMonth == 31);
7221         assert(Date(-1999, 9, 1).daysInMonth == 30);
7222         assert(Date(-1999, 10, 1).daysInMonth == 31);
7223         assert(Date(-1999, 11, 1).daysInMonth == 30);
7224         assert(Date(-1999, 12, 1).daysInMonth == 31);
7225 
7226         const cdate = Date(1999, 7, 6);
7227         immutable idate = Date(1999, 7, 6);
7228         static assert(!__traits(compiles, cdate.daysInMonth = 30));
7229         static assert(!__traits(compiles, idate.daysInMonth = 30));
7230     }
7231 
7232 
7233     /++
7234         Whether the current year is a date in A.D.
7235       +/
7236     @property bool isAD() const @safe pure nothrow @nogc
7237     {
7238         return _year > 0;
7239     }
7240 
7241     ///
7242     @safe unittest
7243     {
7244         assert(Date(1, 1, 1).isAD);
7245         assert(Date(2010, 12, 31).isAD);
7246         assert(!Date(0, 12, 31).isAD);
7247         assert(!Date(-2010, 1, 1).isAD);
7248     }
7249 
7250     @safe unittest
7251     {
7252         assert(Date(2010, 7, 4).isAD);
7253         assert(Date(1, 1, 1).isAD);
7254         assert(!Date(0, 1, 1).isAD);
7255         assert(!Date(-1, 1, 1).isAD);
7256         assert(!Date(-2010, 7, 4).isAD);
7257 
7258         const cdate = Date(1999, 7, 6);
7259         immutable idate = Date(1999, 7, 6);
7260         assert(cdate.isAD);
7261         assert(idate.isAD);
7262     }
7263 
7264 
7265     /++
7266         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
7267         $(LREF Date) at noon (since the Julian day changes at noon).
7268       +/
7269     @property long julianDay() const @safe pure nothrow @nogc
7270     {
7271         return dayOfGregorianCal + 1_721_425;
7272     }
7273 
7274     @safe unittest
7275     {
7276         assert(Date(-4713, 11, 24).julianDay == 0);
7277         assert(Date(0, 12, 31).julianDay == 1_721_425);
7278         assert(Date(1, 1, 1).julianDay == 1_721_426);
7279         assert(Date(1582, 10, 15).julianDay == 2_299_161);
7280         assert(Date(1858, 11, 17).julianDay == 2_400_001);
7281         assert(Date(1982, 1, 4).julianDay == 2_444_974);
7282         assert(Date(1996, 3, 31).julianDay == 2_450_174);
7283         assert(Date(2010, 8, 24).julianDay == 2_455_433);
7284 
7285         const cdate = Date(1999, 7, 6);
7286         immutable idate = Date(1999, 7, 6);
7287         assert(cdate.julianDay == 2_451_366);
7288         assert(idate.julianDay == 2_451_366);
7289     }
7290 
7291 
7292     /++
7293         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
7294         any time on this date (since, the modified Julian day changes at
7295         midnight).
7296       +/
7297     @property long modJulianDay() const @safe pure nothrow @nogc
7298     {
7299         return julianDay - 2_400_001;
7300     }
7301 
7302     @safe unittest
7303     {
7304         assert(Date(1858, 11, 17).modJulianDay == 0);
7305         assert(Date(2010, 8, 24).modJulianDay == 55_432);
7306 
7307         const cdate = Date(1999, 7, 6);
7308         immutable idate = Date(1999, 7, 6);
7309         assert(cdate.modJulianDay == 51_365);
7310         assert(idate.modJulianDay == 51_365);
7311     }
7312 
7313 
7314     /++
7315         Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
7316         If `writer` is set, the resulting string will be written directly
7317         to it.
7318 
7319         Params:
7320             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7321         Returns:
7322             A `string` when not using an output range; `void` otherwise.
7323       +/
7324     string toISOString() const @safe pure nothrow
7325     {
7326         import std.array : appender;
7327         auto w = appender!string();
7328         w.reserve(8);
7329         try
7330             toISOString(w);
7331         catch (Exception e)
7332             assert(0, "toISOString() threw.");
7333         return w.data;
7334     }
7335 
7336     ///
7337     @safe unittest
7338     {
7339         assert(Date(2010, 7, 4).toISOString() == "20100704");
7340         assert(Date(1998, 12, 25).toISOString() == "19981225");
7341         assert(Date(0, 1, 5).toISOString() == "00000105");
7342         assert(Date(-4, 1, 5).toISOString() == "-00040105");
7343     }
7344 
7345     @safe unittest
7346     {
7347         // Test A.D.
7348         assert(Date(9, 12, 4).toISOString() == "00091204");
7349         assert(Date(99, 12, 4).toISOString() == "00991204");
7350         assert(Date(999, 12, 4).toISOString() == "09991204");
7351         assert(Date(9999, 7, 4).toISOString() == "99990704");
7352         assert(Date(10000, 10, 20).toISOString() == "+100001020");
7353 
7354         // Test B.C.
7355         assert(Date(0, 12, 4).toISOString() == "00001204");
7356         assert(Date(-9, 12, 4).toISOString() == "-00091204");
7357         assert(Date(-99, 12, 4).toISOString() == "-00991204");
7358         assert(Date(-999, 12, 4).toISOString() == "-09991204");
7359         assert(Date(-9999, 7, 4).toISOString() == "-99990704");
7360         assert(Date(-10000, 10, 20).toISOString() == "-100001020");
7361 
7362         const cdate = Date(1999, 7, 6);
7363         immutable idate = Date(1999, 7, 6);
7364         assert(cdate.toISOString() == "19990706");
7365         assert(idate.toISOString() == "19990706");
7366     }
7367 
7368     /// ditto
7369     void toISOString(W)(ref W writer) const
7370     if (isOutputRange!(W, char))
7371     {
7372         import std.format.write : formattedWrite;
7373         if (_year >= 0)
7374         {
7375             if (_year < 10_000)
7376                 formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
7377             else
7378                 formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
7379         }
7380         else if (_year > -10_000)
7381             formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
7382         else
7383             formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
7384     }
7385 
7386     @safe pure unittest
7387     {
7388         import std.array : appender;
7389 
7390         auto w = appender!(char[])();
7391         Date(2010, 7, 4).toISOString(w);
7392         assert(w.data == "20100704");
7393         w.clear();
7394         Date(1998, 12, 25).toISOString(w);
7395         assert(w.data == "19981225");
7396     }
7397 
7398     /++
7399         Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
7400         If `writer` is set, the resulting string will be written directly
7401         to it.
7402 
7403         Params:
7404             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7405         Returns:
7406             A `string` when not using an output range; `void` otherwise.
7407       +/
7408     string toISOExtString() const @safe pure nothrow
7409     {
7410         import std.array : appender;
7411         auto w = appender!string();
7412         w.reserve(10);
7413         try
7414             toISOExtString(w);
7415         catch (Exception e)
7416             assert(0, "toISOExtString() threw.");
7417         return w.data;
7418     }
7419 
7420     ///
7421     @safe unittest
7422     {
7423         assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
7424         assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
7425         assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
7426         assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
7427     }
7428 
7429     @safe unittest
7430     {
7431         // Test A.D.
7432         assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
7433         assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
7434         assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
7435         assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
7436         assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
7437 
7438         // Test B.C.
7439         assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
7440         assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
7441         assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
7442         assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
7443         assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
7444         assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
7445 
7446         const cdate = Date(1999, 7, 6);
7447         immutable idate = Date(1999, 7, 6);
7448         assert(cdate.toISOExtString() == "1999-07-06");
7449         assert(idate.toISOExtString() == "1999-07-06");
7450     }
7451 
7452     /// ditto
7453     void toISOExtString(W)(ref W writer) const
7454     if (isOutputRange!(W, char))
7455     {
7456         import std.format.write : formattedWrite;
7457         if (_year >= 0)
7458         {
7459             if (_year < 10_000)
7460                 formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
7461             else
7462                 formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
7463         }
7464         else if (_year > -10_000)
7465             formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
7466         else
7467             formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
7468     }
7469 
7470     @safe pure unittest
7471     {
7472         import std.array : appender;
7473 
7474         auto w = appender!(char[])();
7475         Date(2010, 7, 4).toISOExtString(w);
7476         assert(w.data == "2010-07-04");
7477         w.clear();
7478         Date(-4, 1, 5).toISOExtString(w);
7479         assert(w.data == "-0004-01-05");
7480     }
7481 
7482     /++
7483         Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
7484         If `writer` is set, the resulting string will be written directly
7485         to it.
7486 
7487         Params:
7488             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7489         Returns:
7490             A `string` when not using an output range; `void` otherwise.
7491       +/
7492     string toSimpleString() const @safe pure nothrow
7493     {
7494         import std.array : appender;
7495         auto w = appender!string();
7496         w.reserve(11);
7497         try
7498             toSimpleString(w);
7499         catch (Exception e)
7500             assert(0, "toSimpleString() threw.");
7501         return w.data;
7502     }
7503 
7504     ///
7505     @safe unittest
7506     {
7507         assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
7508         assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
7509         assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
7510         assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
7511     }
7512 
7513     @safe unittest
7514     {
7515         // Test A.D.
7516         assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
7517         assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
7518         assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
7519         assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
7520         assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
7521 
7522         // Test B.C.
7523         assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
7524         assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
7525         assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
7526         assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
7527         assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
7528         assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
7529 
7530         const cdate = Date(1999, 7, 6);
7531         immutable idate = Date(1999, 7, 6);
7532         assert(cdate.toSimpleString() == "1999-Jul-06");
7533         assert(idate.toSimpleString() == "1999-Jul-06");
7534     }
7535 
7536     /// ditto
7537     void toSimpleString(W)(ref W writer) const
7538     if (isOutputRange!(W, char))
7539     {
7540         import std.format.write : formattedWrite;
7541         if (_year >= 0)
7542         {
7543             if (_year < 10_000)
7544                 formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
7545             else
7546                 formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
7547         }
7548         else if (_year > -10_000)
7549             formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
7550         else
7551             formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
7552     }
7553 
7554     @safe pure unittest
7555     {
7556         import std.array : appender;
7557 
7558         auto w = appender!(char[])();
7559         Date(9, 12, 4).toSimpleString(w);
7560         assert(w.data == "0009-Dec-04");
7561         w.clear();
7562         Date(-10000, 10, 20).toSimpleString(w);
7563         assert(w.data == "-10000-Oct-20");
7564     }
7565 
7566     /++
7567         Converts this $(LREF Date) to a string.
7568 
7569         This function exists to make it easy to convert a $(LREF Date) to a
7570         string for code that does not care what the exact format is - just that
7571         it presents the information in a clear manner. It also makes it easy to
7572         simply convert a $(LREF Date) to a string when using functions such as
7573         `to!string`, `format`, or `writeln` which use toString to convert
7574         user-defined types. So, it is unlikely that much code will call
7575         toString directly.
7576 
7577         The format of the string is purposefully unspecified, and code that
7578         cares about the format of the string should use `toISOString`,
7579         `toISOExtString`, `toSimpleString`, or some other custom formatting
7580         function that explicitly generates the format that the code needs. The
7581         reason is that the code is then clear about what format it's using,
7582         making it less error-prone to maintain the code and interact with other
7583         software that consumes the generated strings. It's for this same reason
7584         $(LREF Date) has no `fromString` function, whereas it does have
7585         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
7586 
7587         The format returned by toString may or may not change in the future.
7588       +/
7589     string toString() const @safe pure nothrow
7590     {
7591         return toSimpleString();
7592     }
7593 
7594     @safe unittest
7595     {
7596         auto date = Date(1999, 7, 6);
7597         const cdate = Date(1999, 7, 6);
7598         immutable idate = Date(1999, 7, 6);
7599         assert(date.toString());
7600         assert(cdate.toString());
7601         assert(idate.toString());
7602     }
7603 
7604     /// ditto
7605     void toString(W)(ref W writer) const
7606     if (isOutputRange!(W, char))
7607     {
7608         toSimpleString(writer);
7609     }
7610 
7611     /++
7612         Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
7613         is stripped from the given string.
7614 
7615         Params:
7616             isoString = A string formatted in the ISO format for dates.
7617 
7618         Throws:
7619             $(REF DateTimeException,std,datetime,date) if the given string is
7620             not in the ISO format or if the resulting $(LREF Date) would not be
7621             valid.
7622       +/
7623     static Date fromISOString(S)(scope const S isoString) @safe pure
7624         if (isSomeString!S)
7625     {
7626         import std.algorithm.searching : startsWith;
7627         import std.conv : to, text, ConvException;
7628         import std.exception : enforce;
7629         import std.string : strip;
7630 
7631         auto str = isoString.strip;
7632 
7633         enforce!DateTimeException(str.length >= 8, text("Invalid format for Date.fromISOString: ", isoString));
7634 
7635         int day, month, year;
7636         auto yearStr = str[0 .. $ - 4];
7637 
7638         try
7639         {
7640             // using conversion to uint plus cast because it checks for +/-
7641             // for us quickly while throwing ConvException
7642             day = cast(int) to!uint(str[$ - 2 .. $]);
7643             month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
7644 
7645             if (yearStr.length > 4)
7646             {
7647                 enforce!DateTimeException(yearStr.startsWith('-', '+'),
7648                         text("Invalid format for Date.fromISOString: ", isoString));
7649                 year = to!int(yearStr);
7650             }
7651             else
7652             {
7653                 year = cast(int) to!uint(yearStr);
7654             }
7655         }
7656         catch (ConvException)
7657         {
7658             throw new DateTimeException(text("Invalid format for Date.fromISOString: ", isoString));
7659         }
7660 
7661         return Date(year, month, day);
7662     }
7663 
7664     ///
7665     @safe unittest
7666     {
7667         assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
7668         assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
7669         assert(Date.fromISOString("00000105") == Date(0, 1, 5));
7670         assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
7671         assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
7672     }
7673 
7674     @safe unittest
7675     {
7676         assertThrown!DateTimeException(Date.fromISOString(""));
7677         assertThrown!DateTimeException(Date.fromISOString("990704"));
7678         assertThrown!DateTimeException(Date.fromISOString("0100704"));
7679         assertThrown!DateTimeException(Date.fromISOString("2010070"));
7680         assertThrown!DateTimeException(Date.fromISOString("2010070 "));
7681         assertThrown!DateTimeException(Date.fromISOString("120100704"));
7682         assertThrown!DateTimeException(Date.fromISOString("-0100704"));
7683         assertThrown!DateTimeException(Date.fromISOString("+0100704"));
7684         assertThrown!DateTimeException(Date.fromISOString("2010070a"));
7685         assertThrown!DateTimeException(Date.fromISOString("20100a04"));
7686         assertThrown!DateTimeException(Date.fromISOString("2010a704"));
7687 
7688         assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
7689         assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
7690         assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
7691         assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
7692         assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
7693         assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
7694         assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
7695         assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
7696         assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
7697         assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
7698         assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
7699         assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
7700         assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
7701         assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
7702         assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
7703         assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
7704         assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
7705 
7706         assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
7707         assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
7708         assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
7709         assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
7710         assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
7711         assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
7712         assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
7713         assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
7714         assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
7715         assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
7716 
7717         assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
7718         assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
7719         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
7720         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
7721         assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
7722         assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
7723         assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
7724         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
7725         assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
7726         assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
7727         assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
7728 
7729         assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
7730         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
7731 
7732         assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
7733         assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
7734         assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
7735         assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
7736         assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
7737         assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
7738     }
7739 
7740     // https://issues.dlang.org/show_bug.cgi?id=17801
7741     @safe unittest
7742     {
7743         import std.conv : to;
7744         import std.meta : AliasSeq;
7745         static foreach (C; AliasSeq!(char, wchar, dchar))
7746         {
7747             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7748                 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
7749         }
7750     }
7751 
7752 
7753     /++
7754         Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
7755         Whitespace is stripped from the given string.
7756 
7757         Params:
7758             isoExtString = A string formatted in the ISO Extended format for
7759                            dates.
7760 
7761         Throws:
7762             $(REF DateTimeException,std,datetime,date) if the given string is
7763             not in the ISO Extended format or if the resulting $(LREF Date)
7764             would not be valid.
7765       +/
7766     static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
7767         if (isSomeString!(S))
7768     {
7769         import std.algorithm.searching : startsWith;
7770         import std.conv : to, ConvException;
7771         import std.format : format;
7772         import std.string : strip;
7773 
7774         auto str = strip(isoExtString);
7775         short year;
7776         ubyte month, day;
7777 
7778         if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
7779             throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7780 
7781         auto yearStr = str[0 .. $-6];
7782         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7783         if ((yearStr.length > 4) != signAtBegining)
7784         {
7785             throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7786         }
7787 
7788         try
7789         {
7790             day = to!ubyte(str[$-2 .. $]);
7791             month = to!ubyte(str[$-5 .. $-3]);
7792             year = to!short(yearStr);
7793         }
7794         catch (ConvException)
7795         {
7796             throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7797         }
7798 
7799         return Date(year, month, day);
7800     }
7801 
7802     ///
7803     @safe unittest
7804     {
7805         assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
7806         assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
7807         assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
7808         assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
7809         assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
7810     }
7811 
7812     @safe unittest
7813     {
7814         assertThrown!DateTimeException(Date.fromISOExtString(""));
7815         assertThrown!DateTimeException(Date.fromISOExtString("990704"));
7816         assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
7817         assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
7818         assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
7819         assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
7820         assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
7821         assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
7822         assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
7823         assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
7824         assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
7825 
7826         assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
7827         assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
7828         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
7829         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
7830         assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
7831         assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
7832         assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
7833         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
7834         assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
7835         assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
7836         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
7837         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
7838         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
7839         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
7840         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
7841         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
7842         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
7843 
7844         assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
7845         assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
7846         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
7847         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
7848         assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
7849         assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
7850         assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
7851         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
7852         assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
7853         assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
7854 
7855         assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
7856         assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
7857         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
7858         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
7859         assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
7860         assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
7861         assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
7862         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
7863         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
7864         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
7865         assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
7866 
7867         assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
7868         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
7869 
7870         assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
7871         assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
7872         assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
7873         assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
7874         assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
7875         assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
7876     }
7877 
7878     // https://issues.dlang.org/show_bug.cgi?id=17801
7879     @safe unittest
7880     {
7881         import std.conv : to;
7882         import std.meta : AliasSeq;
7883         static foreach (C; AliasSeq!(char, wchar, dchar))
7884         {
7885             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7886                 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
7887         }
7888     }
7889 
7890 
7891     /++
7892         Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
7893         Whitespace is stripped from the given string.
7894 
7895         Params:
7896             simpleString = A string formatted in the way that toSimpleString
7897                            formats dates.
7898 
7899         Throws:
7900             $(REF DateTimeException,std,datetime,date) if the given string is
7901             not in the correct format or if the resulting $(LREF Date) would not
7902             be valid.
7903       +/
7904     static Date fromSimpleString(S)(scope const S simpleString) @safe pure
7905         if (isSomeString!(S))
7906     {
7907         import std.algorithm.searching : startsWith;
7908         import std.conv : to, ConvException;
7909         import std.format : format;
7910         import std.string : strip;
7911 
7912         auto str = strip(simpleString);
7913 
7914         if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
7915             throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7916 
7917         int year;
7918         uint day;
7919         auto month = monthFromString(str[$ - 6 .. $ - 3]);
7920         auto yearStr = str[0 .. $ - 7];
7921         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7922         if ((yearStr.length > 4) != signAtBegining)
7923         {
7924             throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7925         }
7926 
7927         try
7928         {
7929             day = to!uint(str[$ - 2 .. $]);
7930             year = to!int(yearStr);
7931         }
7932         catch (ConvException)
7933         {
7934             throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7935         }
7936 
7937         return Date(year, month, day);
7938     }
7939 
7940     ///
7941     @safe unittest
7942     {
7943         assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
7944         assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
7945         assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
7946         assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
7947         assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
7948     }
7949 
7950     @safe unittest
7951     {
7952         assertThrown!DateTimeException(Date.fromSimpleString(""));
7953         assertThrown!DateTimeException(Date.fromSimpleString("990704"));
7954         assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
7955         assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
7956         assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
7957         assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
7958         assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
7959         assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
7960         assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
7961         assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
7962         assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
7963 
7964         assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
7965         assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
7966         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
7967         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
7968         assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
7969         assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
7970         assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
7971         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
7972         assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
7973         assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
7974         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
7975         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
7976         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
7977         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
7978         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
7979         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
7980         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
7981 
7982         assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
7983         assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
7984         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
7985         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
7986         assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
7987         assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
7988         assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
7989         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
7990         assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
7991         assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
7992 
7993         assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
7994         assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
7995         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
7996         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
7997         assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
7998         assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
7999         assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
8000         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
8001         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
8002         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
8003         assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
8004 
8005         assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
8006         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
8007 
8008         assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
8009         assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
8010         assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
8011         assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
8012         assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
8013         assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
8014     }
8015 
8016     // https://issues.dlang.org/show_bug.cgi?id=17801
8017     @safe unittest
8018     {
8019         import std.conv : to;
8020         import std.meta : AliasSeq;
8021         static foreach (C; AliasSeq!(char, wchar, dchar))
8022         {
8023             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
8024                 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
8025         }
8026     }
8027 
8028 
8029     /++
8030         Returns the $(LREF Date) farthest in the past which is representable by
8031         $(LREF Date).
8032       +/
8033     @property static Date min() @safe pure nothrow @nogc
8034     {
8035         auto date = Date.init;
8036         date._year = short.min;
8037         date._month = Month.jan;
8038         date._day = 1;
8039 
8040         return date;
8041     }
8042 
8043     @safe unittest
8044     {
8045         assert(Date.min.year < 0);
8046         assert(Date.min < Date.max);
8047     }
8048 
8049 
8050     /++
8051         Returns the $(LREF Date) farthest in the future which is representable
8052         by $(LREF Date).
8053       +/
8054     @property static Date max() @safe pure nothrow @nogc
8055     {
8056         auto date = Date.init;
8057         date._year = short.max;
8058         date._month = Month.dec;
8059         date._day = 31;
8060 
8061         return date;
8062     }
8063 
8064     @safe unittest
8065     {
8066         assert(Date.max.year > 0);
8067         assert(Date.max > Date.min);
8068     }
8069 
8070 
8071 private:
8072 
8073     /+
8074         Whether the given values form a valid date.
8075 
8076         Params:
8077             year  = The year to test.
8078             month = The month of the Gregorian Calendar to test.
8079             day   = The day of the month to test.
8080      +/
8081     static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
8082     {
8083         if (!valid!"months"(month))
8084             return false;
8085         return valid!"days"(year, month, day);
8086     }
8087 
8088 
8089 package:
8090 
8091     /+
8092         Adds the given number of days to this $(LREF Date). A negative number
8093         will subtract.
8094 
8095         The month will be adjusted along with the day if the number of days
8096         added (or subtracted) would overflow (or underflow) the current month.
8097         The year will be adjusted along with the month if the increase (or
8098         decrease) to the month would cause it to overflow (or underflow) the
8099         current year.
8100 
8101         `_addDays(numDays)` is effectively equivalent to
8102         $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
8103 
8104         Params:
8105             days = The number of days to add to this Date.
8106       +/
8107     ref Date _addDays(long days) return @safe pure nothrow @nogc
8108     {
8109         dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
8110         return this;
8111     }
8112 
8113     @safe unittest
8114     {
8115         // Test A.D.
8116         {
8117             auto date = Date(1999, 2, 28);
8118             date._addDays(1);
8119             assert(date == Date(1999, 3, 1));
8120             date._addDays(-1);
8121             assert(date == Date(1999, 2, 28));
8122         }
8123 
8124         {
8125             auto date = Date(2000, 2, 28);
8126             date._addDays(1);
8127             assert(date == Date(2000, 2, 29));
8128             date._addDays(1);
8129             assert(date == Date(2000, 3, 1));
8130             date._addDays(-1);
8131             assert(date == Date(2000, 2, 29));
8132         }
8133 
8134         {
8135             auto date = Date(1999, 6, 30);
8136             date._addDays(1);
8137             assert(date == Date(1999, 7, 1));
8138             date._addDays(-1);
8139             assert(date == Date(1999, 6, 30));
8140         }
8141 
8142         {
8143             auto date = Date(1999, 7, 31);
8144             date._addDays(1);
8145             assert(date == Date(1999, 8, 1));
8146             date._addDays(-1);
8147             assert(date == Date(1999, 7, 31));
8148         }
8149 
8150         {
8151             auto date = Date(1999, 1, 1);
8152             date._addDays(-1);
8153             assert(date == Date(1998, 12, 31));
8154             date._addDays(1);
8155             assert(date == Date(1999, 1, 1));
8156         }
8157 
8158         {
8159             auto date = Date(1999, 7, 6);
8160             date._addDays(9);
8161             assert(date == Date(1999, 7, 15));
8162             date._addDays(-11);
8163             assert(date == Date(1999, 7, 4));
8164             date._addDays(30);
8165             assert(date == Date(1999, 8, 3));
8166             date._addDays(-3);
8167             assert(date == Date(1999, 7, 31));
8168         }
8169 
8170         {
8171             auto date = Date(1999, 7, 6);
8172             date._addDays(365);
8173             assert(date == Date(2000, 7, 5));
8174             date._addDays(-365);
8175             assert(date == Date(1999, 7, 6));
8176             date._addDays(366);
8177             assert(date == Date(2000, 7, 6));
8178             date._addDays(730);
8179             assert(date == Date(2002, 7, 6));
8180             date._addDays(-1096);
8181             assert(date == Date(1999, 7, 6));
8182         }
8183 
8184         // Test B.C.
8185         {
8186             auto date = Date(-1999, 2, 28);
8187             date._addDays(1);
8188             assert(date == Date(-1999, 3, 1));
8189             date._addDays(-1);
8190             assert(date == Date(-1999, 2, 28));
8191         }
8192 
8193         {
8194             auto date = Date(-2000, 2, 28);
8195             date._addDays(1);
8196             assert(date == Date(-2000, 2, 29));
8197             date._addDays(1);
8198             assert(date == Date(-2000, 3, 1));
8199             date._addDays(-1);
8200             assert(date == Date(-2000, 2, 29));
8201         }
8202 
8203         {
8204             auto date = Date(-1999, 6, 30);
8205             date._addDays(1);
8206             assert(date == Date(-1999, 7, 1));
8207             date._addDays(-1);
8208             assert(date == Date(-1999, 6, 30));
8209         }
8210 
8211         {
8212             auto date = Date(-1999, 7, 31);
8213             date._addDays(1);
8214             assert(date == Date(-1999, 8, 1));
8215             date._addDays(-1);
8216             assert(date == Date(-1999, 7, 31));
8217         }
8218 
8219         {
8220             auto date = Date(-1999, 1, 1);
8221             date._addDays(-1);
8222             assert(date == Date(-2000, 12, 31));
8223             date._addDays(1);
8224             assert(date == Date(-1999, 1, 1));
8225         }
8226 
8227         {
8228             auto date = Date(-1999, 7, 6);
8229             date._addDays(9);
8230             assert(date == Date(-1999, 7, 15));
8231             date._addDays(-11);
8232             assert(date == Date(-1999, 7, 4));
8233             date._addDays(30);
8234             assert(date == Date(-1999, 8, 3));
8235             date._addDays(-3);
8236         }
8237 
8238         {
8239             auto date = Date(-1999, 7, 6);
8240             date._addDays(365);
8241             assert(date == Date(-1998, 7, 6));
8242             date._addDays(-365);
8243             assert(date == Date(-1999, 7, 6));
8244             date._addDays(366);
8245             assert(date == Date(-1998, 7, 7));
8246             date._addDays(730);
8247             assert(date == Date(-1996, 7, 6));
8248             date._addDays(-1096);
8249             assert(date == Date(-1999, 7, 6));
8250         }
8251 
8252         // Test Both
8253         {
8254             auto date = Date(1, 7, 6);
8255             date._addDays(-365);
8256             assert(date == Date(0, 7, 6));
8257             date._addDays(365);
8258             assert(date == Date(1, 7, 6));
8259             date._addDays(-731);
8260             assert(date == Date(-1, 7, 6));
8261             date._addDays(730);
8262             assert(date == Date(1, 7, 5));
8263         }
8264 
8265         const cdate = Date(1999, 7, 6);
8266         immutable idate = Date(1999, 7, 6);
8267         static assert(!__traits(compiles, cdate._addDays(12)));
8268         static assert(!__traits(compiles, idate._addDays(12)));
8269     }
8270 
8271 
8272     @safe pure invariant()
8273     {
8274         import std.format : format;
8275         assert(valid!"months"(_month),
8276                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8277         assert(valid!"days"(_year, _month, _day),
8278                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8279     }
8280 
8281     short _year  = 1;
8282     Month _month = Month.jan;
8283     ubyte _day   = 1;
8284 }
8285 
8286 ///
8287 @safe pure unittest
8288 {
8289     import core.time : days;
8290 
8291     auto d = Date(2000, 6, 1);
8292 
8293     assert(d.dayOfYear == 153);
8294     assert(d.dayOfWeek == DayOfWeek.thu);
8295 
8296     d += 10.days;
8297     assert(d == Date(2000, 6, 11));
8298 
8299     assert(d.toISOExtString() == "2000-06-11");
8300     assert(d.toISOString() == "20000611");
8301     assert(d.toSimpleString() == "2000-Jun-11");
8302 
8303     assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
8304     assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
8305     assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
8306 }
8307 
8308 
8309 /++
8310     Represents a time of day with hours, minutes, and seconds. It uses 24 hour
8311     time.
8312 +/
8313 struct TimeOfDay
8314 {
8315 public:
8316 
8317     /++
8318         Params:
8319             hour   = Hour of the day [0 - 24$(RPAREN).
8320             minute = Minute of the hour [0 - 60$(RPAREN).
8321             second = Second of the minute [0 - 60$(RPAREN).
8322 
8323         Throws:
8324             $(REF DateTimeException,std,datetime,date) if the resulting
8325             $(LREF TimeOfDay) would be not be valid.
8326      +/
8327     this(int hour, int minute, int second = 0) @safe pure
8328     {
8329         enforceValid!"hours"(hour);
8330         enforceValid!"minutes"(minute);
8331         enforceValid!"seconds"(second);
8332 
8333         _hour   = cast(ubyte) hour;
8334         _minute = cast(ubyte) minute;
8335         _second = cast(ubyte) second;
8336     }
8337 
8338     @safe unittest
8339     {
8340         assert(TimeOfDay(0, 0) == TimeOfDay.init);
8341 
8342         {
8343             auto tod = TimeOfDay(0, 0);
8344             assert(tod._hour == 0);
8345             assert(tod._minute == 0);
8346             assert(tod._second == 0);
8347         }
8348 
8349         {
8350             auto tod = TimeOfDay(12, 30, 33);
8351             assert(tod._hour == 12);
8352             assert(tod._minute == 30);
8353             assert(tod._second == 33);
8354         }
8355 
8356         {
8357             auto tod = TimeOfDay(23, 59, 59);
8358             assert(tod._hour == 23);
8359             assert(tod._minute == 59);
8360             assert(tod._second == 59);
8361         }
8362 
8363         assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
8364         assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
8365         assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
8366     }
8367 
8368 
8369     /++
8370         Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
8371 
8372         Returns:
8373             $(BOOKTABLE,
8374             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
8375             $(TR $(TD this == rhs) $(TD 0))
8376             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
8377             )
8378      +/
8379     int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
8380     {
8381         if (_hour < rhs._hour)
8382             return -1;
8383         if (_hour > rhs._hour)
8384             return 1;
8385 
8386         if (_minute < rhs._minute)
8387             return -1;
8388         if (_minute > rhs._minute)
8389             return 1;
8390 
8391         if (_second < rhs._second)
8392             return -1;
8393         if (_second > rhs._second)
8394             return 1;
8395 
8396         return 0;
8397     }
8398 
8399     @safe unittest
8400     {
8401         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
8402 
8403         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
8404         assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
8405         assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
8406         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8407 
8408         assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
8409         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
8410 
8411         assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
8412         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8413 
8414         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8415         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8416         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
8417         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8418         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
8419         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
8420 
8421         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8422         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
8423         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
8424         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8425 
8426         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8427         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
8428 
8429         const ctod = TimeOfDay(12, 30, 33);
8430         immutable itod = TimeOfDay(12, 30, 33);
8431         assert(ctod.opCmp(itod) == 0);
8432         assert(itod.opCmp(ctod) == 0);
8433     }
8434 
8435 
8436     /++
8437         Hours past midnight.
8438      +/
8439     @property ubyte hour() const @safe pure nothrow @nogc
8440     {
8441         return _hour;
8442     }
8443 
8444     @safe unittest
8445     {
8446         assert(TimeOfDay.init.hour == 0);
8447         assert(TimeOfDay(12, 0, 0).hour == 12);
8448 
8449         const ctod = TimeOfDay(12, 0, 0);
8450         immutable itod = TimeOfDay(12, 0, 0);
8451         assert(ctod.hour == 12);
8452         assert(itod.hour == 12);
8453     }
8454 
8455 
8456     /++
8457         Hours past midnight.
8458 
8459         Params:
8460             hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
8461 
8462         Throws:
8463             $(REF DateTimeException,std,datetime,date) if the given hour would
8464             result in an invalid $(LREF TimeOfDay).
8465      +/
8466     @property void hour(int hour) @safe pure
8467     {
8468         enforceValid!"hours"(hour);
8469         _hour = cast(ubyte) hour;
8470     }
8471 
8472     @safe unittest
8473     {
8474         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
8475 
8476         auto tod = TimeOfDay(0, 0, 0);
8477         tod.hour = 12;
8478         assert(tod == TimeOfDay(12, 0, 0));
8479 
8480         const ctod = TimeOfDay(0, 0, 0);
8481         immutable itod = TimeOfDay(0, 0, 0);
8482         static assert(!__traits(compiles, ctod.hour = 12));
8483         static assert(!__traits(compiles, itod.hour = 12));
8484     }
8485 
8486 
8487     /++
8488         Minutes past the hour.
8489      +/
8490     @property ubyte minute() const @safe pure nothrow @nogc
8491     {
8492         return _minute;
8493     }
8494 
8495     @safe unittest
8496     {
8497         assert(TimeOfDay.init.minute == 0);
8498         assert(TimeOfDay(0, 30, 0).minute == 30);
8499 
8500         const ctod = TimeOfDay(0, 30, 0);
8501         immutable itod = TimeOfDay(0, 30, 0);
8502         assert(ctod.minute == 30);
8503         assert(itod.minute == 30);
8504     }
8505 
8506 
8507     /++
8508         Minutes past the hour.
8509 
8510         Params:
8511             minute = The minute to set this $(LREF TimeOfDay)'s minute to.
8512 
8513         Throws:
8514             $(REF DateTimeException,std,datetime,date) if the given minute
8515             would result in an invalid $(LREF TimeOfDay).
8516      +/
8517     @property void minute(int minute) @safe pure
8518     {
8519         enforceValid!"minutes"(minute);
8520         _minute = cast(ubyte) minute;
8521     }
8522 
8523     @safe unittest
8524     {
8525         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
8526 
8527         auto tod = TimeOfDay(0, 0, 0);
8528         tod.minute = 30;
8529         assert(tod == TimeOfDay(0, 30, 0));
8530 
8531         const ctod = TimeOfDay(0, 0, 0);
8532         immutable itod = TimeOfDay(0, 0, 0);
8533         static assert(!__traits(compiles, ctod.minute = 30));
8534         static assert(!__traits(compiles, itod.minute = 30));
8535     }
8536 
8537 
8538     /++
8539         Seconds past the minute.
8540      +/
8541     @property ubyte second() const @safe pure nothrow @nogc
8542     {
8543         return _second;
8544     }
8545 
8546     @safe unittest
8547     {
8548         assert(TimeOfDay.init.second == 0);
8549         assert(TimeOfDay(0, 0, 33).second == 33);
8550 
8551         const ctod = TimeOfDay(0, 0, 33);
8552         immutable itod = TimeOfDay(0, 0, 33);
8553         assert(ctod.second == 33);
8554         assert(itod.second == 33);
8555     }
8556 
8557 
8558     /++
8559         Seconds past the minute.
8560 
8561         Params:
8562             second = The second to set this $(LREF TimeOfDay)'s second to.
8563 
8564         Throws:
8565             $(REF DateTimeException,std,datetime,date) if the given second
8566             would result in an invalid $(LREF TimeOfDay).
8567      +/
8568     @property void second(int second) @safe pure
8569     {
8570         enforceValid!"seconds"(second);
8571         _second = cast(ubyte) second;
8572     }
8573 
8574     @safe unittest
8575     {
8576         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
8577 
8578         auto tod = TimeOfDay(0, 0, 0);
8579         tod.second = 33;
8580         assert(tod == TimeOfDay(0, 0, 33));
8581 
8582         const ctod = TimeOfDay(0, 0, 0);
8583         immutable itod = TimeOfDay(0, 0, 0);
8584         static assert(!__traits(compiles, ctod.second = 33));
8585         static assert(!__traits(compiles, itod.second = 33));
8586     }
8587 
8588 
8589     /++
8590         Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
8591         negative number will subtract.
8592 
8593         The difference between rolling and adding is that rolling does not
8594         affect larger units. For instance, rolling a $(LREF TimeOfDay)
8595         one hours's worth of minutes gets the exact same
8596         $(LREF TimeOfDay).
8597 
8598         Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
8599 
8600         Params:
8601             units = The units to add.
8602             value = The number of $(D_PARAM units) to add to this
8603                     $(LREF TimeOfDay).
8604 
8605         Returns:
8606             A reference to the `TimeOfDay` (`this`).
8607       +/
8608     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8609         if (units == "hours")
8610     {
8611         import core.time : dur;
8612         return this += dur!"hours"(value);
8613     }
8614 
8615     ///
8616     @safe unittest
8617     {
8618         auto tod1 = TimeOfDay(7, 12, 0);
8619         tod1.roll!"hours"(1);
8620         assert(tod1 == TimeOfDay(8, 12, 0));
8621 
8622         auto tod2 = TimeOfDay(7, 12, 0);
8623         tod2.roll!"hours"(-1);
8624         assert(tod2 == TimeOfDay(6, 12, 0));
8625 
8626         auto tod3 = TimeOfDay(23, 59, 0);
8627         tod3.roll!"minutes"(1);
8628         assert(tod3 == TimeOfDay(23, 0, 0));
8629 
8630         auto tod4 = TimeOfDay(0, 0, 0);
8631         tod4.roll!"minutes"(-1);
8632         assert(tod4 == TimeOfDay(0, 59, 0));
8633 
8634         auto tod5 = TimeOfDay(23, 59, 59);
8635         tod5.roll!"seconds"(1);
8636         assert(tod5 == TimeOfDay(23, 59, 0));
8637 
8638         auto tod6 = TimeOfDay(0, 0, 0);
8639         tod6.roll!"seconds"(-1);
8640         assert(tod6 == TimeOfDay(0, 0, 59));
8641     }
8642 
8643     @safe unittest
8644     {
8645         auto tod = TimeOfDay(12, 27, 2);
8646         tod.roll!"hours"(22).roll!"hours"(-7);
8647         assert(tod == TimeOfDay(3, 27, 2));
8648 
8649         const ctod = TimeOfDay(0, 0, 0);
8650         immutable itod = TimeOfDay(0, 0, 0);
8651         static assert(!__traits(compiles, ctod.roll!"hours"(53)));
8652         static assert(!__traits(compiles, itod.roll!"hours"(53)));
8653     }
8654 
8655 
8656     /// ditto
8657     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8658         if (units == "minutes" || units == "seconds")
8659     {
8660         import std.format : format;
8661 
8662         enum memberVarStr = units[0 .. $ - 1];
8663         value %= 60;
8664         mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
8665 
8666         if (value < 0)
8667         {
8668             if (newVal < 0)
8669                 newVal += 60;
8670         }
8671         else if (newVal >= 60)
8672             newVal -= 60;
8673 
8674         mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
8675         return this;
8676     }
8677 
8678     // Test roll!"minutes"().
8679     @safe unittest
8680     {
8681         static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
8682         {
8683             orig.roll!"minutes"(minutes);
8684             assert(orig == expected);
8685         }
8686 
8687         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8688         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
8689         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
8690         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
8691         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
8692         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
8693         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
8694         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
8695         testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
8696         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
8697         testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
8698         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8699         testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
8700         testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
8701         testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
8702 
8703         testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
8704         testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
8705         testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
8706         testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
8707         testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
8708         testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
8709         testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
8710         testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
8711 
8712         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
8713         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
8714         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
8715         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
8716         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
8717         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
8718         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
8719         testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
8720         testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
8721         testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
8722         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8723         testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
8724         testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
8725         testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
8726 
8727         testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
8728         testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
8729         testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
8730         testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
8731         testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
8732         testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
8733         testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
8734         testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
8735 
8736         testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
8737         testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
8738         testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
8739 
8740         testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
8741         testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
8742         testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
8743 
8744         testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
8745         testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
8746         testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
8747 
8748         testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
8749         testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
8750         testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
8751 
8752         auto tod = TimeOfDay(12, 27, 2);
8753         tod.roll!"minutes"(97).roll!"minutes"(-102);
8754         assert(tod == TimeOfDay(12, 22, 2));
8755 
8756         const ctod = TimeOfDay(0, 0, 0);
8757         immutable itod = TimeOfDay(0, 0, 0);
8758         static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
8759         static assert(!__traits(compiles, itod.roll!"minutes"(7)));
8760     }
8761 
8762     // Test roll!"seconds"().
8763     @safe unittest
8764     {
8765         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
8766         {
8767             orig.roll!"seconds"(seconds);
8768             assert(orig == expected);
8769         }
8770 
8771         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8772         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
8773         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
8774         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
8775         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
8776         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
8777         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
8778         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
8779         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
8780         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
8781         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
8782         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
8783         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8784         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
8785 
8786         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
8787         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
8788         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
8789         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
8790         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
8791         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
8792         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
8793         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
8794 
8795         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
8796         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
8797         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
8798         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
8799         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
8800         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
8801         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
8802         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
8803         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
8804         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
8805         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
8806         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8807         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
8808 
8809         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
8810         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
8811         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
8812 
8813         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
8814         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
8815         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
8816 
8817         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
8818         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
8819         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
8820 
8821         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
8822         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
8823         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
8824 
8825         auto tod = TimeOfDay(12, 27, 2);
8826         tod.roll!"seconds"(105).roll!"seconds"(-77);
8827         assert(tod == TimeOfDay(12, 27, 30));
8828 
8829         const ctod = TimeOfDay(0, 0, 0);
8830         immutable itod = TimeOfDay(0, 0, 0);
8831         static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
8832         static assert(!__traits(compiles, itod.roll!"seconds"(7)));
8833     }
8834 
8835 
8836     import core.time : Duration;
8837     /++
8838         Gives the result of adding or subtracting a $(REF Duration, core,time)
8839         from this $(LREF TimeOfDay).
8840 
8841         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8842         are
8843 
8844         $(BOOKTABLE,
8845         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8846         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8847         )
8848 
8849         Params:
8850             duration = The $(REF Duration, core,time) to add to or subtract from
8851                        this $(LREF TimeOfDay).
8852       +/
8853     TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
8854         if (op == "+" || op == "-")
8855     {
8856         TimeOfDay retval = this;
8857         immutable seconds = duration.total!"seconds";
8858         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
8859     }
8860 
8861     ///
8862     @safe unittest
8863     {
8864         import core.time : hours, minutes, seconds;
8865 
8866         assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
8867         assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
8868         assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
8869         assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
8870 
8871         assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
8872         assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
8873         assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
8874         assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
8875     }
8876 
8877     @safe unittest
8878     {
8879         auto tod = TimeOfDay(12, 30, 33);
8880 
8881         import core.time : dur;
8882         assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8883         assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8884         assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8885         assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8886         assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8887         assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8888 
8889         assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8890         assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8891         assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8892         assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8893         assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8894         assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8895 
8896         assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8897         assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8898         assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8899         assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8900         assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8901         assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8902 
8903         assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8904         assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8905         assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8906         assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8907         assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8908         assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8909 
8910         auto duration = dur!"hours"(11);
8911         const ctod = TimeOfDay(12, 30, 33);
8912         immutable itod = TimeOfDay(12, 30, 33);
8913         assert(tod + duration == TimeOfDay(23, 30, 33));
8914         assert(ctod + duration == TimeOfDay(23, 30, 33));
8915         assert(itod + duration == TimeOfDay(23, 30, 33));
8916 
8917         assert(tod - duration == TimeOfDay(1, 30, 33));
8918         assert(ctod - duration == TimeOfDay(1, 30, 33));
8919         assert(itod - duration == TimeOfDay(1, 30, 33));
8920     }
8921 
8922 
8923     /++
8924         Gives the result of adding or subtracting a $(REF Duration, core,time)
8925         from this $(LREF TimeOfDay), as well as assigning the result to this
8926         $(LREF TimeOfDay).
8927 
8928         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8929         are
8930 
8931         $(BOOKTABLE,
8932         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8933         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8934         )
8935 
8936         Params:
8937             duration = The $(REF Duration, core,time) to add to or subtract from
8938                        this $(LREF TimeOfDay).
8939       +/
8940     ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
8941         if (op == "+" || op == "-")
8942     {
8943         immutable seconds = duration.total!"seconds";
8944         mixin("return _addSeconds(" ~ op ~ "seconds);");
8945     }
8946 
8947     @safe unittest
8948     {
8949         import core.time : dur;
8950         auto duration = dur!"hours"(12);
8951 
8952         assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8953         assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8954         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8955         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8956         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8957         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8958 
8959         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8960         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8961         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8962         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8963         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8964         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8965 
8966         assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8967         assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8968         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8969         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8970         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8971         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8972 
8973         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8974         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8975         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8976         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8977         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8978         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8979 
8980         auto tod = TimeOfDay(19, 17, 22);
8981         (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
8982         assert(tod == TimeOfDay(17, 15, 59));
8983 
8984         const ctod = TimeOfDay(12, 33, 30);
8985         immutable itod = TimeOfDay(12, 33, 30);
8986         static assert(!__traits(compiles, ctod += duration));
8987         static assert(!__traits(compiles, itod += duration));
8988         static assert(!__traits(compiles, ctod -= duration));
8989         static assert(!__traits(compiles, itod -= duration));
8990     }
8991 
8992 
8993     /++
8994         Gives the difference between two $(LREF TimeOfDay)s.
8995 
8996         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8997         are
8998 
8999         $(BOOKTABLE,
9000         $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
9001         )
9002 
9003         Params:
9004             rhs = The $(LREF TimeOfDay) to subtract from this one.
9005       +/
9006     Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
9007         if (op == "-")
9008     {
9009         immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
9010         immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
9011 
9012         import core.time : dur;
9013         return dur!"seconds"(lhsSec - rhsSec);
9014     }
9015 
9016     @safe unittest
9017     {
9018         auto tod = TimeOfDay(12, 30, 33);
9019 
9020         import core.time : dur;
9021         assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
9022         assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
9023         assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
9024         assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
9025         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
9026         assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
9027         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
9028         assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
9029 
9030         const ctod = TimeOfDay(12, 30, 33);
9031         immutable itod = TimeOfDay(12, 30, 33);
9032         assert(tod - tod == Duration.zero);
9033         assert(ctod - tod == Duration.zero);
9034         assert(itod - tod == Duration.zero);
9035 
9036         assert(tod - ctod == Duration.zero);
9037         assert(ctod - ctod == Duration.zero);
9038         assert(itod - ctod == Duration.zero);
9039 
9040         assert(tod - itod == Duration.zero);
9041         assert(ctod - itod == Duration.zero);
9042         assert(itod - itod == Duration.zero);
9043     }
9044 
9045 
9046     /++
9047         Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
9048         If `writer` is set, the resulting string will be written directly to it.
9049 
9050         Params:
9051             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9052         Returns:
9053             A `string` when not using an output range; `void` otherwise.
9054       +/
9055     string toISOString() const @safe pure nothrow
9056     {
9057         import std.array : appender;
9058         auto w = appender!string();
9059         w.reserve(6);
9060         try
9061             toISOString(w);
9062         catch (Exception e)
9063             assert(0, "toISOString() threw.");
9064         return w.data;
9065     }
9066 
9067     /// ditto
9068     void toISOString(W)(ref W writer) const
9069     if (isOutputRange!(W, char))
9070     {
9071         import std.format.write : formattedWrite;
9072         formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
9073     }
9074 
9075     ///
9076     @safe unittest
9077     {
9078         assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
9079         assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
9080     }
9081 
9082     @safe unittest
9083     {
9084         auto tod = TimeOfDay(12, 30, 33);
9085         const ctod = TimeOfDay(12, 30, 33);
9086         immutable itod = TimeOfDay(12, 30, 33);
9087         assert(tod.toISOString() == "123033");
9088         assert(ctod.toISOString() == "123033");
9089         assert(itod.toISOString() == "123033");
9090     }
9091 
9092 
9093     /++
9094         Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
9095         If `writer` is set, the resulting string will be written directly to it.
9096 
9097         Params:
9098             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9099         Returns:
9100             A `string` when not using an output range; `void` otherwise.
9101       +/
9102     string toISOExtString() const @safe pure nothrow
9103     {
9104         import std.array : appender;
9105         auto w = appender!string();
9106         w.reserve(8);
9107         try
9108             toISOExtString(w);
9109         catch (Exception e)
9110             assert(0, "toISOExtString() threw.");
9111         return w.data;
9112     }
9113 
9114     /// ditto
9115     void toISOExtString(W)(ref W writer) const
9116     if (isOutputRange!(W, char))
9117     {
9118         import std.format.write : formattedWrite;
9119         formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
9120     }
9121 
9122     ///
9123     @safe unittest
9124     {
9125         assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
9126         assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
9127     }
9128 
9129     @safe unittest
9130     {
9131         auto tod = TimeOfDay(12, 30, 33);
9132         const ctod = TimeOfDay(12, 30, 33);
9133         immutable itod = TimeOfDay(12, 30, 33);
9134         assert(tod.toISOExtString() == "12:30:33");
9135         assert(ctod.toISOExtString() == "12:30:33");
9136         assert(itod.toISOExtString() == "12:30:33");
9137     }
9138 
9139 
9140     /++
9141         Converts this TimeOfDay to a string.
9142 
9143         This function exists to make it easy to convert a $(LREF TimeOfDay) to a
9144         string for code that does not care what the exact format is - just that
9145         it presents the information in a clear manner. It also makes it easy to
9146         simply convert a $(LREF TimeOfDay) to a string when using functions such
9147         as `to!string`, `format`, or `writeln` which use toString to convert
9148         user-defined types. So, it is unlikely that much code will call
9149         toString directly.
9150 
9151         The format of the string is purposefully unspecified, and code that
9152         cares about the format of the string should use `toISOString`,
9153         `toISOExtString`, or some other custom formatting function that
9154         explicitly generates the format that the code needs. The reason is that
9155         the code is then clear about what format it's using, making it less
9156         error-prone to maintain the code and interact with other software that
9157         consumes the generated strings. It's for this same reason that
9158         $(LREF TimeOfDay) has no `fromString` function, whereas it does have
9159         `fromISOString` and `fromISOExtString`.
9160 
9161         The format returned by toString may or may not change in the future.
9162 
9163         Params:
9164             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9165         Returns:
9166             A `string` when not using an output range; `void` otherwise.
9167       +/
9168     string toString() const @safe pure nothrow
9169     {
9170         return toISOExtString();
9171     }
9172 
9173     /// ditto
9174     void toString(W)(ref W writer) const
9175     if (isOutputRange!(W, char))
9176     {
9177         toISOExtString(writer);
9178     }
9179 
9180     @safe unittest
9181     {
9182         auto tod = TimeOfDay(12, 30, 33);
9183         const ctod = TimeOfDay(12, 30, 33);
9184         immutable itod = TimeOfDay(12, 30, 33);
9185         assert(tod.toString());
9186         assert(ctod.toString());
9187         assert(itod.toString());
9188     }
9189 
9190 
9191     /++
9192         Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
9193         Whitespace is stripped from the given string.
9194 
9195         Params:
9196             isoString = A string formatted in the ISO format for times.
9197 
9198         Throws:
9199             $(REF DateTimeException,std,datetime,date) if the given string is
9200             not in the ISO format or if the resulting $(LREF TimeOfDay) would
9201             not be valid.
9202       +/
9203     static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
9204         if (isSomeString!S)
9205     {
9206         import std.conv : to, text, ConvException;
9207         import std.exception : enforce;
9208         import std.string : strip;
9209 
9210         int hours, minutes, seconds;
9211         auto str = strip(isoString);
9212 
9213         enforce!DateTimeException(str.length == 6, text("Invalid format for TimeOfDay.fromISOString: ", isoString));
9214 
9215         try
9216         {
9217             // cast to int from uint is used because it checks for
9218             // non digits without extra loops
9219             hours = cast(int) to!uint(str[0 .. 2]);
9220             minutes = cast(int) to!uint(str[2 .. 4]);
9221             seconds = cast(int) to!uint(str[4 .. $]);
9222         }
9223         catch (ConvException)
9224         {
9225             throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOString: ", isoString));
9226         }
9227 
9228         return TimeOfDay(hours, minutes, seconds);
9229     }
9230 
9231     ///
9232     @safe unittest
9233     {
9234         assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
9235         assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
9236         assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
9237     }
9238 
9239     @safe unittest
9240     {
9241         assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
9242         assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
9243         assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
9244         assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
9245         assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
9246         assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
9247         assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
9248         assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
9249         assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
9250         assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
9251         assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
9252         assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
9253         assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
9254         assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
9255         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
9256         assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
9257         assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
9258         assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
9259         assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
9260         assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
9261         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
9262         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
9263 
9264         assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
9265         assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
9266         assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
9267         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
9268         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
9269         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
9270         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
9271         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
9272         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
9273         assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
9274         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
9275         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
9276         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
9277         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
9278         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
9279         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
9280         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
9281         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
9282         assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
9283         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
9284         assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
9285         assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
9286         assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
9287         assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
9288         assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
9289         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
9290         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
9291 
9292         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
9293 
9294         assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
9295         assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
9296         assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
9297         assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
9298         assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
9299         assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
9300     }
9301 
9302     // https://issues.dlang.org/show_bug.cgi?id=17801
9303     @safe unittest
9304     {
9305         import std.conv : to;
9306         import std.meta : AliasSeq;
9307         static foreach (C; AliasSeq!(char, wchar, dchar))
9308         {
9309             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9310                 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
9311         }
9312     }
9313 
9314 
9315     /++
9316         Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
9317         Whitespace is stripped from the given string.
9318 
9319         Params:
9320             isoExtString = A string formatted in the ISO Extended format for
9321             times.
9322 
9323         Throws:
9324             $(REF DateTimeException,std,datetime,date) if the given string is
9325             not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
9326             would not be valid.
9327       +/
9328     static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
9329         if (isSomeString!S)
9330     {
9331         import std.conv : ConvException, text, to;
9332         import std.string : strip;
9333 
9334         auto str = strip(isoExtString);
9335         int hours, minutes, seconds;
9336 
9337         if (str.length != 8 || str[2] != ':' || str[5] != ':')
9338             throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOExtString: ", isoExtString));
9339 
9340         try
9341         {
9342             // cast to int from uint is used because it checks for
9343             // non digits without extra loops
9344             hours = cast(int) to!uint(str[0 .. 2]);
9345             minutes = cast(int) to!uint(str[3 .. 5]);
9346             seconds = cast(int) to!uint(str[6 .. $]);
9347         }
9348         catch (ConvException)
9349         {
9350             throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOExtString: ", isoExtString));
9351         }
9352 
9353         return TimeOfDay(hours, minutes, seconds);
9354     }
9355 
9356     ///
9357     @safe unittest
9358     {
9359         assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
9360         assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
9361         assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
9362     }
9363 
9364     @safe unittest
9365     {
9366         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
9367         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
9368         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
9369         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
9370         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
9371         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
9372         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
9373         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
9374         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
9375         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
9376         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
9377         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
9378         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
9379         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
9380         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
9381         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
9382         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
9383         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
9384         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
9385         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
9386         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
9387         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
9388 
9389         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
9390         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
9391         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
9392         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
9393         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
9394         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
9395         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
9396         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
9397         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
9398         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
9399         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
9400         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
9401         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
9402         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
9403         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
9404         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
9405         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
9406         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
9407         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
9408         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
9409         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
9410         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
9411         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
9412         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
9413         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
9414         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
9415         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
9416 
9417         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
9418 
9419         assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
9420         assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
9421         assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
9422         assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
9423         assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
9424         assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
9425     }
9426 
9427     // https://issues.dlang.org/show_bug.cgi?id=17801
9428     @safe unittest
9429     {
9430         import std.conv : to;
9431         import std.meta : AliasSeq;
9432         static foreach (C; AliasSeq!(char, wchar, dchar))
9433         {
9434             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9435                 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
9436         }
9437     }
9438 
9439 
9440     /++
9441         Returns midnight.
9442       +/
9443     @property static TimeOfDay min() @safe pure nothrow @nogc
9444     {
9445         return TimeOfDay.init;
9446     }
9447 
9448     @safe unittest
9449     {
9450         assert(TimeOfDay.min.hour == 0);
9451         assert(TimeOfDay.min.minute == 0);
9452         assert(TimeOfDay.min.second == 0);
9453         assert(TimeOfDay.min < TimeOfDay.max);
9454     }
9455 
9456 
9457     /++
9458         Returns one second short of midnight.
9459       +/
9460     @property static TimeOfDay max() @safe pure nothrow @nogc
9461     {
9462         auto tod = TimeOfDay.init;
9463         tod._hour = maxHour;
9464         tod._minute = maxMinute;
9465         tod._second = maxSecond;
9466 
9467         return tod;
9468     }
9469 
9470     @safe unittest
9471     {
9472         assert(TimeOfDay.max.hour == 23);
9473         assert(TimeOfDay.max.minute == 59);
9474         assert(TimeOfDay.max.second == 59);
9475         assert(TimeOfDay.max > TimeOfDay.min);
9476     }
9477 
9478 
9479 private:
9480 
9481     /+
9482         Add seconds to the time of day. Negative values will subtract. If the
9483         number of seconds overflows (or underflows), then the seconds will wrap,
9484         increasing (or decreasing) the number of minutes accordingly. If the
9485         number of minutes overflows (or underflows), then the minutes will wrap.
9486         If the number of minutes overflows(or underflows), then the hour will
9487         wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
9488 
9489         Params:
9490             seconds = The number of seconds to add to this TimeOfDay.
9491       +/
9492     ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
9493     {
9494         import core.time : convert;
9495         long hnsecs = convert!("seconds", "hnsecs")(seconds);
9496         hnsecs += convert!("hours", "hnsecs")(_hour);
9497         hnsecs += convert!("minutes", "hnsecs")(_minute);
9498         hnsecs += convert!("seconds", "hnsecs")(_second);
9499 
9500         hnsecs %= convert!("days", "hnsecs")(1);
9501 
9502         if (hnsecs < 0)
9503             hnsecs += convert!("days", "hnsecs")(1);
9504 
9505         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
9506         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9507         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
9508 
9509         _hour = cast(ubyte) newHours;
9510         _minute = cast(ubyte) newMinutes;
9511         _second = cast(ubyte) newSeconds;
9512 
9513         return this;
9514     }
9515 
9516     @safe unittest
9517     {
9518         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
9519         {
9520             orig._addSeconds(seconds);
9521             assert(orig == expected);
9522         }
9523 
9524         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
9525         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
9526         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
9527         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
9528         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
9529         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
9530         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
9531         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
9532         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
9533         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
9534         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
9535         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
9536         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
9537         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
9538 
9539         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
9540         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
9541         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
9542         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
9543         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
9544         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
9545         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
9546         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
9547 
9548         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
9549         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
9550         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
9551         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
9552         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
9553         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
9554         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
9555         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
9556         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
9557         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
9558         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
9559         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
9560         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
9561 
9562         testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
9563         testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
9564         testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
9565         testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
9566         testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
9567         testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
9568 
9569         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
9570         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
9571         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
9572 
9573         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
9574         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
9575         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
9576 
9577         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
9578         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
9579         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
9580 
9581         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
9582         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
9583         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
9584 
9585         const ctod = TimeOfDay(0, 0, 0);
9586         immutable itod = TimeOfDay(0, 0, 0);
9587         static assert(!__traits(compiles, ctod._addSeconds(7)));
9588         static assert(!__traits(compiles, itod._addSeconds(7)));
9589     }
9590 
9591 
9592     /+
9593         Whether the given values form a valid $(LREF TimeOfDay).
9594      +/
9595     static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
9596     {
9597         return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
9598     }
9599 
9600 
9601     @safe pure invariant()
9602     {
9603         import std.format : format;
9604         assert(_valid(_hour, _minute, _second),
9605                format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
9606     }
9607 
9608 
9609 package:
9610 
9611     ubyte _hour;
9612     ubyte _minute;
9613     ubyte _second;
9614 
9615     enum ubyte maxHour   = 24 - 1;
9616     enum ubyte maxMinute = 60 - 1;
9617     enum ubyte maxSecond = 60 - 1;
9618 }
9619 
9620 ///
9621 @safe pure unittest
9622 {
9623     import core.time : minutes, seconds;
9624 
9625     auto t = TimeOfDay(12, 30, 0);
9626 
9627     t += 10.minutes + 100.seconds;
9628     assert(t == TimeOfDay(12, 41, 40));
9629 
9630     assert(t.toISOExtString() == "12:41:40");
9631     assert(t.toISOString() == "124140");
9632 
9633     assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
9634     assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
9635 }
9636 
9637 /++
9638     Returns whether the given value is valid for the given unit type when in a
9639     time point. Naturally, a duration is not held to a particular range, but
9640     the values in a time point are (e.g. a month must be in the range of
9641     1 - 12 inclusive).
9642 
9643     Params:
9644         units = The units of time to validate.
9645         value = The number to validate.
9646   +/
9647 bool valid(string units)(int value) @safe pure nothrow @nogc
9648 if (units == "months" ||
9649     units == "hours" ||
9650     units == "minutes" ||
9651     units == "seconds")
9652 {
9653     static if (units == "months")
9654         return value >= Month.jan && value <= Month.dec;
9655     else static if (units == "hours")
9656         return value >= 0 && value <= 23;
9657     else static if (units == "minutes")
9658         return value >= 0 && value <= 59;
9659     else static if (units == "seconds")
9660         return value >= 0 && value <= 59;
9661 }
9662 
9663 ///
9664 @safe unittest
9665 {
9666     assert(valid!"hours"(12));
9667     assert(!valid!"hours"(32));
9668     assert(valid!"months"(12));
9669     assert(!valid!"months"(13));
9670 }
9671 
9672 /++
9673     Returns whether the given day is valid for the given year and month.
9674 
9675     Params:
9676         units = The units of time to validate.
9677         year  = The year of the day to validate.
9678         month = The month of the day to validate (January is 1).
9679         day   = The day to validate.
9680   +/
9681 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9682 if (units == "days")
9683 {
9684     return day > 0 && day <= maxDay(year, month);
9685 }
9686 
9687 ///
9688 @safe pure nothrow @nogc unittest
9689 {
9690     assert(valid!"days"(2016, 2, 29));
9691     assert(!valid!"days"(2016, 2, 30));
9692     assert(valid!"days"(2017, 2, 20));
9693     assert(!valid!"days"(2017, 2, 29));
9694 }
9695 
9696 private short castToYear(int year, string file = __FILE__, size_t line = __LINE__) @safe pure
9697 {
9698     import std.conv : to, ConvOverflowException;
9699     import std.format : format;
9700 
9701     try
9702         return year.to!short;
9703     catch (ConvOverflowException)
9704         throw new DateTimeException(format("year %s doesn't fit to Date.", year), file, line);
9705 }
9706 
9707 /++
9708     Params:
9709         units = The units of time to validate.
9710         value = The number to validate.
9711         file  = The file that the $(LREF DateTimeException) will list if thrown.
9712         line  = The line number that the $(LREF DateTimeException) will list if
9713                 thrown.
9714 
9715     Throws:
9716         $(LREF DateTimeException) if `valid!units(value)` is false.
9717   +/
9718 void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9719 if (units == "months" ||
9720     units == "hours" ||
9721     units == "minutes" ||
9722     units == "seconds")
9723 {
9724     import std.format : format;
9725 
9726     static if (units == "months")
9727     {
9728         if (!valid!units(value))
9729             throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
9730     }
9731     else static if (units == "hours")
9732     {
9733         if (!valid!units(value))
9734             throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
9735     }
9736     else static if (units == "minutes")
9737     {
9738         if (!valid!units(value))
9739             throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
9740     }
9741     else static if (units == "seconds")
9742     {
9743         if (!valid!units(value))
9744             throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
9745     }
9746 }
9747 
9748 ///
9749 @safe pure unittest
9750 {
9751     import std.exception : assertThrown, assertNotThrown;
9752 
9753     assertNotThrown(enforceValid!"months"(10));
9754     assertNotThrown(enforceValid!"seconds"(40));
9755 
9756     assertThrown!DateTimeException(enforceValid!"months"(0));
9757     assertThrown!DateTimeException(enforceValid!"hours"(24));
9758     assertThrown!DateTimeException(enforceValid!"minutes"(60));
9759     assertThrown!DateTimeException(enforceValid!"seconds"(60));
9760 }
9761 
9762 
9763 /++
9764     Because the validity of the day number depends on both on the year
9765     and month of which the day is occurring, take all three variables
9766     to validate the day.
9767 
9768     Params:
9769         units = The units of time to validate.
9770         year  = The year of the day to validate.
9771         month = The month of the day to validate.
9772         day   = The day to validate.
9773         file  = The file that the $(LREF DateTimeException) will list if thrown.
9774         line  = The line number that the $(LREF DateTimeException) will list if
9775                 thrown.
9776 
9777     Throws:
9778         $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
9779   +/
9780 void enforceValid(string units)
9781                  (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9782 if (units == "days")
9783 {
9784     import std.format : format;
9785     if (!valid!"days"(year, month, day))
9786         throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
9787 }
9788 
9789 ///
9790 @safe pure unittest
9791 {
9792     import std.exception : assertThrown, assertNotThrown;
9793 
9794     assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
9795     // leap year
9796     assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
9797 
9798     assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
9799     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
9800     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
9801 }
9802 
9803 
9804 /++
9805     Returns the number of days from the current day of the week to the given
9806     day of the week. If they are the same, then the result is 0.
9807 
9808     Params:
9809         currDoW = The current day of the week.
9810         dow     = The day of the week to get the number of days to.
9811   +/
9812 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
9813 {
9814     if (currDoW == dow)
9815         return 0;
9816     if (currDoW < dow)
9817         return dow - currDoW;
9818     return DayOfWeek.sat - currDoW + dow + 1;
9819 }
9820 
9821 ///
9822 @safe pure nothrow @nogc unittest
9823 {
9824     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9825     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9826     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9827 }
9828 
9829 @safe unittest
9830 {
9831     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
9832     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
9833     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
9834     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
9835     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
9836     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
9837     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
9838 
9839     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9840     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9841     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
9842     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9843     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
9844     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
9845     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
9846 
9847     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
9848     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
9849     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
9850     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
9851     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
9852     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
9853     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
9854 
9855     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
9856     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
9857     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
9858     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
9859     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
9860     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
9861     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
9862 
9863     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
9864     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
9865     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
9866     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
9867     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
9868     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
9869     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
9870 
9871     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
9872     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
9873     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
9874     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
9875     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
9876     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
9877     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
9878 
9879     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
9880     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
9881     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
9882     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
9883     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
9884     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
9885     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
9886 }
9887 
9888 
9889 /++
9890     Returns the number of months from the current months of the year to the
9891     given month of the year. If they are the same, then the result is 0.
9892 
9893     Params:
9894         currMonth = The current month of the year.
9895         month     = The month of the year to get the number of months to.
9896   +/
9897 int monthsToMonth(int currMonth, int month) @safe pure
9898 {
9899     enforceValid!"months"(currMonth);
9900     enforceValid!"months"(month);
9901 
9902     if (currMonth == month)
9903         return 0;
9904     if (currMonth < month)
9905         return month - currMonth;
9906     return Month.dec - currMonth + month;
9907 }
9908 
9909 ///
9910 @safe pure unittest
9911 {
9912     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9913     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9914     assert(monthsToMonth(Month.jul, Month.oct) == 3);
9915 }
9916 
9917 @safe unittest
9918 {
9919     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9920     assert(monthsToMonth(Month.jan, Month.feb) == 1);
9921     assert(monthsToMonth(Month.jan, Month.mar) == 2);
9922     assert(monthsToMonth(Month.jan, Month.apr) == 3);
9923     assert(monthsToMonth(Month.jan, Month.may) == 4);
9924     assert(monthsToMonth(Month.jan, Month.jun) == 5);
9925     assert(monthsToMonth(Month.jan, Month.jul) == 6);
9926     assert(monthsToMonth(Month.jan, Month.aug) == 7);
9927     assert(monthsToMonth(Month.jan, Month.sep) == 8);
9928     assert(monthsToMonth(Month.jan, Month.oct) == 9);
9929     assert(monthsToMonth(Month.jan, Month.nov) == 10);
9930     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9931 
9932     assert(monthsToMonth(Month.may, Month.jan) == 8);
9933     assert(monthsToMonth(Month.may, Month.feb) == 9);
9934     assert(monthsToMonth(Month.may, Month.mar) == 10);
9935     assert(monthsToMonth(Month.may, Month.apr) == 11);
9936     assert(monthsToMonth(Month.may, Month.may) == 0);
9937     assert(monthsToMonth(Month.may, Month.jun) == 1);
9938     assert(monthsToMonth(Month.may, Month.jul) == 2);
9939     assert(monthsToMonth(Month.may, Month.aug) == 3);
9940     assert(monthsToMonth(Month.may, Month.sep) == 4);
9941     assert(monthsToMonth(Month.may, Month.oct) == 5);
9942     assert(monthsToMonth(Month.may, Month.nov) == 6);
9943     assert(monthsToMonth(Month.may, Month.dec) == 7);
9944 
9945     assert(monthsToMonth(Month.oct, Month.jan) == 3);
9946     assert(monthsToMonth(Month.oct, Month.feb) == 4);
9947     assert(monthsToMonth(Month.oct, Month.mar) == 5);
9948     assert(monthsToMonth(Month.oct, Month.apr) == 6);
9949     assert(monthsToMonth(Month.oct, Month.may) == 7);
9950     assert(monthsToMonth(Month.oct, Month.jun) == 8);
9951     assert(monthsToMonth(Month.oct, Month.jul) == 9);
9952     assert(monthsToMonth(Month.oct, Month.aug) == 10);
9953     assert(monthsToMonth(Month.oct, Month.sep) == 11);
9954     assert(monthsToMonth(Month.oct, Month.oct) == 0);
9955     assert(monthsToMonth(Month.oct, Month.nov) == 1);
9956     assert(monthsToMonth(Month.oct, Month.dec) == 2);
9957 
9958     assert(monthsToMonth(Month.dec, Month.jan) == 1);
9959     assert(monthsToMonth(Month.dec, Month.feb) == 2);
9960     assert(monthsToMonth(Month.dec, Month.mar) == 3);
9961     assert(monthsToMonth(Month.dec, Month.apr) == 4);
9962     assert(monthsToMonth(Month.dec, Month.may) == 5);
9963     assert(monthsToMonth(Month.dec, Month.jun) == 6);
9964     assert(monthsToMonth(Month.dec, Month.jul) == 7);
9965     assert(monthsToMonth(Month.dec, Month.aug) == 8);
9966     assert(monthsToMonth(Month.dec, Month.sep) == 9);
9967     assert(monthsToMonth(Month.dec, Month.oct) == 10);
9968     assert(monthsToMonth(Month.dec, Month.nov) == 11);
9969     assert(monthsToMonth(Month.dec, Month.dec) == 0);
9970 }
9971 
9972 
9973 /++
9974     Whether the given Gregorian Year is a leap year.
9975 
9976     Params:
9977         year = The year to to be tested.
9978  +/
9979 bool yearIsLeapYear(int year) @safe pure nothrow @nogc
9980 {
9981     if (year % 400 == 0)
9982         return true;
9983     if (year % 100 == 0)
9984         return false;
9985     return year % 4 == 0;
9986 }
9987 
9988 ///
9989 @safe unittest
9990 {
9991     foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
9992     {
9993         assert(!yearIsLeapYear(year));
9994         assert(!yearIsLeapYear(-year));
9995     }
9996 
9997     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
9998     {
9999         assert(yearIsLeapYear(year));
10000         assert(yearIsLeapYear(-year));
10001     }
10002 }
10003 
10004 @safe unittest
10005 {
10006     import std.format : format;
10007     foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
10008                     2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
10009     {
10010         assert(!yearIsLeapYear(year), format("year: %s.", year));
10011         assert(!yearIsLeapYear(-year), format("year: %s.", year));
10012     }
10013 
10014     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
10015     {
10016         assert(yearIsLeapYear(year), format("year: %s.", year));
10017         assert(yearIsLeapYear(-year), format("year: %s.", year));
10018     }
10019 }
10020 
10021 
10022 /++
10023     Whether the given type defines all of the necessary functions for it to
10024     function as a time point.
10025 
10026     1. `T` must define a static property named `min` which is the smallest
10027        value of `T` as `Unqual!T`.
10028 
10029     2. `T` must define a static property named `max` which is the largest
10030        value of `T` as `Unqual!T`.
10031 
10032     3. `T` must define an `opBinary` for addition and subtraction that
10033        accepts $(REF Duration, core,time) and returns `Unqual!T`.
10034 
10035     4. `T` must define an `opOpAssign` for addition and subtraction that
10036        accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
10037 
10038     5. `T` must define a `opBinary` for subtraction which accepts `T`
10039        and returns $(REF Duration, core,time).
10040   +/
10041 template isTimePoint(T)
10042 {
10043     import core.time : Duration;
10044     import std.traits : FunctionAttribute, functionAttributes, Unqual;
10045 
10046     enum isTimePoint = hasMin &&
10047                        hasMax &&
10048                        hasOverloadedOpBinaryWithDuration &&
10049                        hasOverloadedOpAssignWithDuration &&
10050                        hasOverloadedOpBinaryWithSelf &&
10051                        !is(U == Duration);
10052 
10053 private:
10054 
10055     alias U = Unqual!T;
10056 
10057     enum hasMin = __traits(hasMember, T, "min") &&
10058                   is(typeof(T.min) == U) &&
10059                   is(typeof({static assert(__traits(isStaticFunction, T.min));}));
10060 
10061     enum hasMax = __traits(hasMember, T, "max") &&
10062                   is(typeof(T.max) == U) &&
10063                   is(typeof({static assert(__traits(isStaticFunction, T.max));}));
10064 
10065     enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
10066                                              is(typeof(T.init - Duration.init) == U);
10067 
10068     enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
10069                                              is(typeof(U.init -= Duration.init) == U) &&
10070                                              is(typeof(
10071                                              {
10072                                                  alias add = U.opOpAssign!"+";
10073                                                  alias sub = U.opOpAssign!"-";
10074                                                  alias FA = FunctionAttribute;
10075                                                  static assert((functionAttributes!add & FA.ref_) != 0);
10076                                                  static assert((functionAttributes!sub & FA.ref_) != 0);
10077                                              }));
10078 
10079     enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
10080 }
10081 
10082 ///
10083 @safe unittest
10084 {
10085     import core.time : Duration;
10086     import std.datetime.interval : Interval;
10087     import std.datetime.systime : SysTime;
10088 
10089     static assert(isTimePoint!Date);
10090     static assert(isTimePoint!DateTime);
10091     static assert(isTimePoint!SysTime);
10092     static assert(isTimePoint!TimeOfDay);
10093 
10094     static assert(!isTimePoint!int);
10095     static assert(!isTimePoint!Duration);
10096     static assert(!isTimePoint!(Interval!SysTime));
10097 }
10098 
10099 @safe unittest
10100 {
10101     import core.time;
10102     import std.datetime.interval;
10103     import std.datetime.systime;
10104     import std.meta : AliasSeq;
10105 
10106     static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
10107     {
10108         static assert(isTimePoint!(const TP), TP.stringof);
10109         static assert(isTimePoint!(immutable TP), TP.stringof);
10110     }
10111 
10112     static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
10113         static assert(!isTimePoint!T, T.stringof);
10114 }
10115 
10116 
10117 /++
10118     Whether all of the given strings are valid units of time.
10119 
10120     `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
10121     can handle precision greater than hnsecs, and the few functions in core.time
10122     which deal with "nsecs" deal with it explicitly.
10123   +/
10124 bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
10125 {
10126     import std.algorithm.searching : canFind;
10127     foreach (str; units)
10128     {
10129         if (!canFind(timeStrings[], str))
10130             return false;
10131     }
10132     return true;
10133 }
10134 
10135 ///
10136 @safe @nogc nothrow unittest
10137 {
10138     assert(validTimeUnits("msecs", "seconds", "minutes"));
10139     assert(validTimeUnits("days", "weeks", "months"));
10140     assert(!validTimeUnits("ms", "seconds", "minutes"));
10141 }
10142 
10143 
10144 /++
10145     Compares two time unit strings. `"years"` are the largest units and
10146     `"hnsecs"` are the smallest.
10147 
10148     Returns:
10149         $(BOOKTABLE,
10150         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10151         $(TR $(TD this == rhs) $(TD 0))
10152         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10153         )
10154 
10155     Throws:
10156         $(LREF DateTimeException) if either of the given strings is not a valid
10157         time unit string.
10158  +/
10159 int cmpTimeUnits(string lhs, string rhs) @safe pure
10160 {
10161     import std.algorithm.searching : countUntil;
10162     import std.exception : enforce;
10163     import std.format : format;
10164 
10165     immutable indexOfLHS = countUntil(timeStrings, lhs);
10166     immutable indexOfRHS = countUntil(timeStrings, rhs);
10167 
10168     enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
10169     enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
10170 
10171     if (indexOfLHS < indexOfRHS)
10172         return -1;
10173     if (indexOfLHS > indexOfRHS)
10174         return 1;
10175 
10176     return 0;
10177 }
10178 
10179 ///
10180 @safe pure unittest
10181 {
10182     import std.exception : assertThrown;
10183 
10184     assert(cmpTimeUnits("hours", "hours") == 0);
10185     assert(cmpTimeUnits("hours", "weeks") < 0);
10186     assert(cmpTimeUnits("months", "seconds") > 0);
10187 
10188     assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
10189 }
10190 
10191 @safe unittest
10192 {
10193     foreach (i, outerUnits; timeStrings)
10194     {
10195         assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
10196 
10197         // For some reason, $ won't compile.
10198         foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
10199             assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
10200     }
10201 
10202     foreach (i, outerUnits; timeStrings)
10203     {
10204         foreach (innerUnits; timeStrings[0 .. i])
10205             assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
10206     }
10207 }
10208 
10209 
10210 /++
10211     Compares two time unit strings at compile time. `"years"` are the largest
10212     units and `"hnsecs"` are the smallest.
10213 
10214     This template is used instead of `cmpTimeUnits` because exceptions
10215     can't be thrown at compile time and `cmpTimeUnits` must enforce that
10216     the strings it's given are valid time unit strings. This template uses a
10217     template constraint instead.
10218 
10219     Returns:
10220         $(BOOKTABLE,
10221         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10222         $(TR $(TD this == rhs) $(TD 0))
10223         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10224         )
10225  +/
10226 template CmpTimeUnits(string lhs, string rhs)
10227 if (validTimeUnits(lhs, rhs))
10228 {
10229     enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
10230 }
10231 
10232 ///
10233 @safe pure unittest
10234 {
10235     static assert(CmpTimeUnits!("years", "weeks") > 0);
10236     static assert(CmpTimeUnits!("days", "days") == 0);
10237     static assert(CmpTimeUnits!("seconds", "hours") < 0);
10238 }
10239 
10240 // Helper function for CmpTimeUnits.
10241 private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
10242 {
10243     import std.algorithm.searching : countUntil;
10244     auto tstrings = timeStrings;
10245     immutable indexOfLHS = countUntil(tstrings, lhs);
10246     immutable indexOfRHS = countUntil(tstrings, rhs);
10247 
10248     if (indexOfLHS < indexOfRHS)
10249         return -1;
10250     if (indexOfLHS > indexOfRHS)
10251         return 1;
10252 
10253     return 0;
10254 }
10255 
10256 @safe unittest
10257 {
10258     static foreach (i; 0 .. timeStrings.length)
10259     {
10260         static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
10261 
10262         static foreach (next; timeStrings[i + 1 .. $])
10263             static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
10264 
10265         static foreach (prev; timeStrings[0 .. i])
10266             static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
10267     }
10268 }
10269 
10270 
10271 package:
10272 
10273 
10274 /+
10275     Array of the short (three letter) names of each month.
10276   +/
10277 immutable string[12] _monthNames = ["Jan",
10278                                     "Feb",
10279                                     "Mar",
10280                                     "Apr",
10281                                     "May",
10282                                     "Jun",
10283                                     "Jul",
10284                                     "Aug",
10285                                     "Sep",
10286                                     "Oct",
10287                                     "Nov",
10288                                     "Dec"];
10289 
10290 /+
10291     The maximum valid Day in the given month in the given year.
10292 
10293     Params:
10294         year  = The year to get the day for.
10295         month = The month of the Gregorian Calendar to get the day for.
10296  +/
10297 ubyte maxDay(int year, int month) @safe pure nothrow @nogc
10298 in
10299 {
10300     assert(valid!"months"(month));
10301 }
10302 do
10303 {
10304     switch (month)
10305     {
10306         case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
10307             return 31;
10308         case Month.feb:
10309             return yearIsLeapYear(year) ? 29 : 28;
10310         case Month.apr, Month.jun, Month.sep, Month.nov:
10311             return 30;
10312         default:
10313             assert(0, "Invalid month.");
10314     }
10315 }
10316 
10317 @safe unittest
10318 {
10319     // Test A.D.
10320     assert(maxDay(1999, 1) == 31);
10321     assert(maxDay(1999, 2) == 28);
10322     assert(maxDay(1999, 3) == 31);
10323     assert(maxDay(1999, 4) == 30);
10324     assert(maxDay(1999, 5) == 31);
10325     assert(maxDay(1999, 6) == 30);
10326     assert(maxDay(1999, 7) == 31);
10327     assert(maxDay(1999, 8) == 31);
10328     assert(maxDay(1999, 9) == 30);
10329     assert(maxDay(1999, 10) == 31);
10330     assert(maxDay(1999, 11) == 30);
10331     assert(maxDay(1999, 12) == 31);
10332 
10333     assert(maxDay(2000, 1) == 31);
10334     assert(maxDay(2000, 2) == 29);
10335     assert(maxDay(2000, 3) == 31);
10336     assert(maxDay(2000, 4) == 30);
10337     assert(maxDay(2000, 5) == 31);
10338     assert(maxDay(2000, 6) == 30);
10339     assert(maxDay(2000, 7) == 31);
10340     assert(maxDay(2000, 8) == 31);
10341     assert(maxDay(2000, 9) == 30);
10342     assert(maxDay(2000, 10) == 31);
10343     assert(maxDay(2000, 11) == 30);
10344     assert(maxDay(2000, 12) == 31);
10345 
10346     // Test B.C.
10347     assert(maxDay(-1999, 1) == 31);
10348     assert(maxDay(-1999, 2) == 28);
10349     assert(maxDay(-1999, 3) == 31);
10350     assert(maxDay(-1999, 4) == 30);
10351     assert(maxDay(-1999, 5) == 31);
10352     assert(maxDay(-1999, 6) == 30);
10353     assert(maxDay(-1999, 7) == 31);
10354     assert(maxDay(-1999, 8) == 31);
10355     assert(maxDay(-1999, 9) == 30);
10356     assert(maxDay(-1999, 10) == 31);
10357     assert(maxDay(-1999, 11) == 30);
10358     assert(maxDay(-1999, 12) == 31);
10359 
10360     assert(maxDay(-2000, 1) == 31);
10361     assert(maxDay(-2000, 2) == 29);
10362     assert(maxDay(-2000, 3) == 31);
10363     assert(maxDay(-2000, 4) == 30);
10364     assert(maxDay(-2000, 5) == 31);
10365     assert(maxDay(-2000, 6) == 30);
10366     assert(maxDay(-2000, 7) == 31);
10367     assert(maxDay(-2000, 8) == 31);
10368     assert(maxDay(-2000, 9) == 30);
10369     assert(maxDay(-2000, 10) == 31);
10370     assert(maxDay(-2000, 11) == 30);
10371     assert(maxDay(-2000, 12) == 31);
10372 }
10373 
10374 /+
10375     Splits out a particular unit from hnsecs and gives the value for that
10376     unit and the remaining hnsecs. It really shouldn't be used unless unless
10377     all units larger than the given units have already been split out.
10378 
10379     Params:
10380         units  = The units to split out.
10381         hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
10382                  after splitting out the given units.
10383 
10384     Returns:
10385         The number of the given units from converting hnsecs to those units.
10386   +/
10387 long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
10388 if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
10389 {
10390     import core.time : convert;
10391     immutable value = convert!("hnsecs", units)(hnsecs);
10392     hnsecs -= convert!(units, "hnsecs")(value);
10393     return value;
10394 }
10395 
10396 @safe unittest
10397 {
10398     auto hnsecs = 2595000000007L;
10399     immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
10400     assert(days == 3);
10401     assert(hnsecs == 3000000007);
10402 
10403     immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
10404     assert(minutes == 5);
10405     assert(hnsecs == 7);
10406 }
10407 
10408 
10409 /+
10410     Returns the day of the week for the given day of the Gregorian Calendar.
10411 
10412     Params:
10413         day = The day of the Gregorian Calendar for which to get the day of
10414               the week.
10415   +/
10416 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
10417 {
10418     // January 1st, 1 A.D. was a Monday
10419     if (day >= 0)
10420         return cast(DayOfWeek)(day % 7);
10421     else
10422     {
10423         immutable dow = cast(DayOfWeek)((day % 7) + 7);
10424 
10425         if (dow == 7)
10426             return DayOfWeek.sun;
10427         else
10428             return dow;
10429     }
10430 }
10431 
10432 @safe unittest
10433 {
10434     import std.datetime.systime : SysTime;
10435 
10436     // Test A.D.
10437     assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
10438     assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
10439     assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
10440     assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
10441     assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
10442     assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
10443     assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
10444     assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
10445     assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
10446     assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
10447     assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
10448     assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
10449     assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10450     assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10451     assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
10452     assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
10453     assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
10454     assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
10455     assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
10456     assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
10457     assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
10458     assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
10459 
10460     // Test B.C.
10461     assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
10462     assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
10463     assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
10464     assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
10465     assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
10466     assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
10467     assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
10468     assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
10469     assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
10470 }
10471 
10472 
10473 private:
10474 
10475 enum daysInYear     = 365;  // The number of days in a non-leap year.
10476 enum daysInLeapYear = 366;  // The numbef or days in a leap year.
10477 enum daysIn4Years   = daysInYear * 3 + daysInLeapYear;  // Number of days in 4 years.
10478 enum daysIn100Years = daysIn4Years * 25 - 1;  // The number of days in 100 years.
10479 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
10480 
10481 /+
10482     Array of integers representing the last days of each month in a year.
10483   +/
10484 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
10485 
10486 /+
10487     Array of integers representing the last days of each month in a leap year.
10488   +/
10489 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
10490 
10491 
10492 /+
10493     Returns the string representation of the given month.
10494   +/
10495 string monthToString(Month month) @safe pure
10496 {
10497     import std.format : format;
10498     assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
10499     return _monthNames[month - Month.jan];
10500 }
10501 
10502 @safe unittest
10503 {
10504     assert(monthToString(Month.jan) == "Jan");
10505     assert(monthToString(Month.feb) == "Feb");
10506     assert(monthToString(Month.mar) == "Mar");
10507     assert(monthToString(Month.apr) == "Apr");
10508     assert(monthToString(Month.may) == "May");
10509     assert(monthToString(Month.jun) == "Jun");
10510     assert(monthToString(Month.jul) == "Jul");
10511     assert(monthToString(Month.aug) == "Aug");
10512     assert(monthToString(Month.sep) == "Sep");
10513     assert(monthToString(Month.oct) == "Oct");
10514     assert(monthToString(Month.nov) == "Nov");
10515     assert(monthToString(Month.dec) == "Dec");
10516 }
10517 
10518 
10519 /+
10520     Returns the Month corresponding to the given string.
10521 
10522     Params:
10523         monthStr = The string representation of the month to get the Month for.
10524 
10525     Throws:
10526         $(REF DateTimeException,std,datetime,date) if the given month is not a
10527         valid month string.
10528   +/
10529 Month monthFromString(T)(T monthStr) @safe pure
10530 if (isSomeString!T)
10531 {
10532     import std.format : format;
10533     switch (monthStr)
10534     {
10535         case "Jan":
10536             return Month.jan;
10537         case "Feb":
10538             return Month.feb;
10539         case "Mar":
10540             return Month.mar;
10541         case "Apr":
10542             return Month.apr;
10543         case "May":
10544             return Month.may;
10545         case "Jun":
10546             return Month.jun;
10547         case "Jul":
10548             return Month.jul;
10549         case "Aug":
10550             return Month.aug;
10551         case "Sep":
10552             return Month.sep;
10553         case "Oct":
10554             return Month.oct;
10555         case "Nov":
10556             return Month.nov;
10557         case "Dec":
10558             return Month.dec;
10559         default:
10560             throw new DateTimeException(format!"Invalid month %s"(monthStr));
10561     }
10562 }
10563 
10564 @safe unittest
10565 {
10566     import std.conv : to;
10567     import std.traits : EnumMembers;
10568     foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
10569                       "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
10570     {
10571         assertThrown!DateTimeException(monthFromString(badStr), badStr);
10572     }
10573 
10574     foreach (month; EnumMembers!Month)
10575     {
10576         assert(monthFromString(monthToString(month)) == month, month.to!string);
10577     }
10578 }
10579 
10580 
10581 // NOTE: all the non-simple array literals are wrapped in functions, because
10582 // otherwise importing causes re-evaluation of the static initializers using
10583 // CTFE with unittests enabled
10584 version (StdUnittest)
10585 {
10586 private @safe:
10587     // All of these helper arrays are sorted in ascending order.
10588     auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
10589     auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
10590 
10591     // I'd use a Tuple, but I get forward reference errors if I try.
10592     struct MonthDay
10593     {
10594         Month month;
10595         short day;
10596 
10597         this(int m, short d)
10598         {
10599             month = cast(Month) m;
10600             day = d;
10601         }
10602     }
10603 
10604     MonthDay[] testMonthDays()
10605     {
10606         static MonthDay[] result = [MonthDay(1, 1),
10607                                 MonthDay(1, 2),
10608                                 MonthDay(3, 17),
10609                                 MonthDay(7, 4),
10610                                 MonthDay(10, 27),
10611                                 MonthDay(12, 30),
10612                                 MonthDay(12, 31)];
10613         return result;
10614     }
10615 
10616     auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
10617 
10618     TimeOfDay[] testTODs()
10619     {
10620         static result = [TimeOfDay(0, 0, 0),
10621                      TimeOfDay(0, 0, 1),
10622                      TimeOfDay(0, 1, 0),
10623                      TimeOfDay(1, 0, 0),
10624                      TimeOfDay(13, 13, 13),
10625                      TimeOfDay(23, 59, 59)];
10626         return result;
10627     }
10628 
10629     auto testHours = [0, 1, 12, 22, 23];
10630     auto testMinSecs = [0, 1, 30, 58, 59];
10631 
10632     // Throwing exceptions is incredibly expensive, so we want to use a smaller
10633     // set of values for tests using assertThrown.
10634     TimeOfDay[] testTODsThrown()
10635     {
10636        static result = [TimeOfDay(0, 0, 0),
10637                            TimeOfDay(13, 13, 13),
10638                            TimeOfDay(23, 59, 59)];
10639        return result;
10640     }
10641 
10642     Date[] testDatesBC;
10643     Date[] testDatesAD;
10644 
10645     DateTime[] testDateTimesBC;
10646     DateTime[] testDateTimesAD;
10647 
10648     // I'd use a Tuple, but I get forward reference errors if I try.
10649     struct GregDay { int day; Date date; }
10650     GregDay[] testGregDaysBC()
10651     {
10652        static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
10653                            GregDay(-735_233, Date(-2012, 1, 1)),
10654                            GregDay(-735_202, Date(-2012, 2, 1)),
10655                            GregDay(-735_175, Date(-2012, 2, 28)),
10656                            GregDay(-735_174, Date(-2012, 2, 29)),
10657                            GregDay(-735_173, Date(-2012, 3, 1)),
10658                            GregDay(-734_502, Date(-2010, 1, 1)),
10659                            GregDay(-734_472, Date(-2010, 1, 31)),
10660                            GregDay(-734_471, Date(-2010, 2, 1)),
10661                            GregDay(-734_444, Date(-2010, 2, 28)),
10662                            GregDay(-734_443, Date(-2010, 3, 1)),
10663                            GregDay(-734_413, Date(-2010, 3, 31)),
10664                            GregDay(-734_412, Date(-2010, 4, 1)),
10665                            GregDay(-734_383, Date(-2010, 4, 30)),
10666                            GregDay(-734_382, Date(-2010, 5, 1)),
10667                            GregDay(-734_352, Date(-2010, 5, 31)),
10668                            GregDay(-734_351, Date(-2010, 6, 1)),
10669                            GregDay(-734_322, Date(-2010, 6, 30)),
10670                            GregDay(-734_321, Date(-2010, 7, 1)),
10671                            GregDay(-734_291, Date(-2010, 7, 31)),
10672                            GregDay(-734_290, Date(-2010, 8, 1)),
10673                            GregDay(-734_260, Date(-2010, 8, 31)),
10674                            GregDay(-734_259, Date(-2010, 9, 1)),
10675                            GregDay(-734_230, Date(-2010, 9, 30)),
10676                            GregDay(-734_229, Date(-2010, 10, 1)),
10677                            GregDay(-734_199, Date(-2010, 10, 31)),
10678                            GregDay(-734_198, Date(-2010, 11, 1)),
10679                            GregDay(-734_169, Date(-2010, 11, 30)),
10680                            GregDay(-734_168, Date(-2010, 12, 1)),
10681                            GregDay(-734_139, Date(-2010, 12, 30)),
10682                            GregDay(-734_138, Date(-2010, 12, 31)),
10683                            GregDay(-731_215, Date(-2001, 1, 1)),
10684                            GregDay(-730_850, Date(-2000, 1, 1)),
10685                            GregDay(-730_849, Date(-2000, 1, 2)),
10686                            GregDay(-730_486, Date(-2000, 12, 30)),
10687                            GregDay(-730_485, Date(-2000, 12, 31)),
10688                            GregDay(-730_484, Date(-1999, 1, 1)),
10689                            GregDay(-694_690, Date(-1901, 1, 1)),
10690                            GregDay(-694_325, Date(-1900, 1, 1)),
10691                            GregDay(-585_118, Date(-1601, 1, 1)),
10692                            GregDay(-584_753, Date(-1600, 1, 1)),
10693                            GregDay(-584_388, Date(-1600, 12, 31)),
10694                            GregDay(-584_387, Date(-1599, 1, 1)),
10695                            GregDay(-365_972, Date(-1001, 1, 1)),
10696                            GregDay(-365_607, Date(-1000, 1, 1)),
10697                            GregDay(-183_351, Date(-501, 1, 1)),
10698                            GregDay(-182_986, Date(-500, 1, 1)),
10699                            GregDay(-182_621, Date(-499, 1, 1)),
10700                            GregDay(-146_827, Date(-401, 1, 1)),
10701                            GregDay(-146_462, Date(-400, 1, 1)),
10702                            GregDay(-146_097, Date(-400, 12, 31)),
10703                            GregDay(-110_302, Date(-301, 1, 1)),
10704                            GregDay(-109_937, Date(-300, 1, 1)),
10705                            GregDay(-73_778, Date(-201, 1, 1)),
10706                            GregDay(-73_413, Date(-200, 1, 1)),
10707                            GregDay(-38_715, Date(-105, 1, 1)),
10708                            GregDay(-37_254, Date(-101, 1, 1)),
10709                            GregDay(-36_889, Date(-100, 1, 1)),
10710                            GregDay(-36_524, Date(-99, 1, 1)),
10711                            GregDay(-36_160, Date(-99, 12, 31)),
10712                            GregDay(-35_794, Date(-97, 1, 1)),
10713                            GregDay(-18_627, Date(-50, 1, 1)),
10714                            GregDay(-18_262, Date(-49, 1, 1)),
10715                            GregDay(-3652, Date(-9, 1, 1)),
10716                            GregDay(-2191, Date(-5, 1, 1)),
10717                            GregDay(-1827, Date(-5, 12, 31)),
10718                            GregDay(-1826, Date(-4, 1, 1)),
10719                            GregDay(-1825, Date(-4, 1, 2)),
10720                            GregDay(-1462, Date(-4, 12, 30)),
10721                            GregDay(-1461, Date(-4, 12, 31)),
10722                            GregDay(-1460, Date(-3, 1, 1)),
10723                            GregDay(-1096, Date(-3, 12, 31)),
10724                            GregDay(-1095, Date(-2, 1, 1)),
10725                            GregDay(-731, Date(-2, 12, 31)),
10726                            GregDay(-730, Date(-1, 1, 1)),
10727                            GregDay(-367, Date(-1, 12, 30)),
10728                            GregDay(-366, Date(-1, 12, 31)),
10729                            GregDay(-365, Date(0, 1, 1)),
10730                            GregDay(-31, Date(0, 11, 30)),
10731                            GregDay(-30, Date(0, 12, 1)),
10732                            GregDay(-1, Date(0, 12, 30)),
10733                            GregDay(0, Date(0, 12, 31))];
10734        return result;
10735     }
10736 
10737     GregDay[] testGregDaysAD()
10738     {
10739        static result = [GregDay(1, Date(1, 1, 1)),
10740                            GregDay(2, Date(1, 1, 2)),
10741                            GregDay(32, Date(1, 2, 1)),
10742                            GregDay(365, Date(1, 12, 31)),
10743                            GregDay(366, Date(2, 1, 1)),
10744                            GregDay(731, Date(3, 1, 1)),
10745                            GregDay(1096, Date(4, 1, 1)),
10746                            GregDay(1097, Date(4, 1, 2)),
10747                            GregDay(1460, Date(4, 12, 30)),
10748                            GregDay(1461, Date(4, 12, 31)),
10749                            GregDay(1462, Date(5, 1, 1)),
10750                            GregDay(17_898, Date(50, 1, 1)),
10751                            GregDay(35_065, Date(97, 1, 1)),
10752                            GregDay(36_160, Date(100, 1, 1)),
10753                            GregDay(36_525, Date(101, 1, 1)),
10754                            GregDay(37_986, Date(105, 1, 1)),
10755                            GregDay(72_684, Date(200, 1, 1)),
10756                            GregDay(73_049, Date(201, 1, 1)),
10757                            GregDay(109_208, Date(300, 1, 1)),
10758                            GregDay(109_573, Date(301, 1, 1)),
10759                            GregDay(145_732, Date(400, 1, 1)),
10760                            GregDay(146_098, Date(401, 1, 1)),
10761                            GregDay(182_257, Date(500, 1, 1)),
10762                            GregDay(182_622, Date(501, 1, 1)),
10763                            GregDay(364_878, Date(1000, 1, 1)),
10764                            GregDay(365_243, Date(1001, 1, 1)),
10765                            GregDay(584_023, Date(1600, 1, 1)),
10766                            GregDay(584_389, Date(1601, 1, 1)),
10767                            GregDay(693_596, Date(1900, 1, 1)),
10768                            GregDay(693_961, Date(1901, 1, 1)),
10769                            GregDay(729_755, Date(1999, 1, 1)),
10770                            GregDay(730_120, Date(2000, 1, 1)),
10771                            GregDay(730_121, Date(2000, 1, 2)),
10772                            GregDay(730_484, Date(2000, 12, 30)),
10773                            GregDay(730_485, Date(2000, 12, 31)),
10774                            GregDay(730_486, Date(2001, 1, 1)),
10775                            GregDay(733_773, Date(2010, 1, 1)),
10776                            GregDay(733_774, Date(2010, 1, 2)),
10777                            GregDay(733_803, Date(2010, 1, 31)),
10778                            GregDay(733_804, Date(2010, 2, 1)),
10779                            GregDay(733_831, Date(2010, 2, 28)),
10780                            GregDay(733_832, Date(2010, 3, 1)),
10781                            GregDay(733_862, Date(2010, 3, 31)),
10782                            GregDay(733_863, Date(2010, 4, 1)),
10783                            GregDay(733_892, Date(2010, 4, 30)),
10784                            GregDay(733_893, Date(2010, 5, 1)),
10785                            GregDay(733_923, Date(2010, 5, 31)),
10786                            GregDay(733_924, Date(2010, 6, 1)),
10787                            GregDay(733_953, Date(2010, 6, 30)),
10788                            GregDay(733_954, Date(2010, 7, 1)),
10789                            GregDay(733_984, Date(2010, 7, 31)),
10790                            GregDay(733_985, Date(2010, 8, 1)),
10791                            GregDay(734_015, Date(2010, 8, 31)),
10792                            GregDay(734_016, Date(2010, 9, 1)),
10793                            GregDay(734_045, Date(2010, 9, 30)),
10794                            GregDay(734_046, Date(2010, 10, 1)),
10795                            GregDay(734_076, Date(2010, 10, 31)),
10796                            GregDay(734_077, Date(2010, 11, 1)),
10797                            GregDay(734_106, Date(2010, 11, 30)),
10798                            GregDay(734_107, Date(2010, 12, 1)),
10799                            GregDay(734_136, Date(2010, 12, 30)),
10800                            GregDay(734_137, Date(2010, 12, 31)),
10801                            GregDay(734_503, Date(2012, 1, 1)),
10802                            GregDay(734_534, Date(2012, 2, 1)),
10803                            GregDay(734_561, Date(2012, 2, 28)),
10804                            GregDay(734_562, Date(2012, 2, 29)),
10805                            GregDay(734_563, Date(2012, 3, 1)),
10806                            GregDay(734_858, Date(2012, 12, 21))];
10807        return result;
10808     }
10809 
10810     // I'd use a Tuple, but I get forward reference errors if I try.
10811     struct DayOfYear { int day; MonthDay md; }
10812     DayOfYear[] testDaysOfYear()
10813     {
10814        static result = [DayOfYear(1, MonthDay(1, 1)),
10815                            DayOfYear(2, MonthDay(1, 2)),
10816                            DayOfYear(3, MonthDay(1, 3)),
10817                            DayOfYear(31, MonthDay(1, 31)),
10818                            DayOfYear(32, MonthDay(2, 1)),
10819                            DayOfYear(59, MonthDay(2, 28)),
10820                            DayOfYear(60, MonthDay(3, 1)),
10821                            DayOfYear(90, MonthDay(3, 31)),
10822                            DayOfYear(91, MonthDay(4, 1)),
10823                            DayOfYear(120, MonthDay(4, 30)),
10824                            DayOfYear(121, MonthDay(5, 1)),
10825                            DayOfYear(151, MonthDay(5, 31)),
10826                            DayOfYear(152, MonthDay(6, 1)),
10827                            DayOfYear(181, MonthDay(6, 30)),
10828                            DayOfYear(182, MonthDay(7, 1)),
10829                            DayOfYear(212, MonthDay(7, 31)),
10830                            DayOfYear(213, MonthDay(8, 1)),
10831                            DayOfYear(243, MonthDay(8, 31)),
10832                            DayOfYear(244, MonthDay(9, 1)),
10833                            DayOfYear(273, MonthDay(9, 30)),
10834                            DayOfYear(274, MonthDay(10, 1)),
10835                            DayOfYear(304, MonthDay(10, 31)),
10836                            DayOfYear(305, MonthDay(11, 1)),
10837                            DayOfYear(334, MonthDay(11, 30)),
10838                            DayOfYear(335, MonthDay(12, 1)),
10839                            DayOfYear(363, MonthDay(12, 29)),
10840                            DayOfYear(364, MonthDay(12, 30)),
10841                            DayOfYear(365, MonthDay(12, 31))];
10842        return result;
10843     }
10844 
10845     DayOfYear[] testDaysOfLeapYear()
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(2, 29)),
10854                                DayOfYear(61, MonthDay(3, 1)),
10855                                DayOfYear(91, MonthDay(3, 31)),
10856                                DayOfYear(92, MonthDay(4, 1)),
10857                                DayOfYear(121, MonthDay(4, 30)),
10858                                DayOfYear(122, MonthDay(5, 1)),
10859                                DayOfYear(152, MonthDay(5, 31)),
10860                                DayOfYear(153, MonthDay(6, 1)),
10861                                DayOfYear(182, MonthDay(6, 30)),
10862                                DayOfYear(183, MonthDay(7, 1)),
10863                                DayOfYear(213, MonthDay(7, 31)),
10864                                DayOfYear(214, MonthDay(8, 1)),
10865                                DayOfYear(244, MonthDay(8, 31)),
10866                                DayOfYear(245, MonthDay(9, 1)),
10867                                DayOfYear(274, MonthDay(9, 30)),
10868                                DayOfYear(275, MonthDay(10, 1)),
10869                                DayOfYear(305, MonthDay(10, 31)),
10870                                DayOfYear(306, MonthDay(11, 1)),
10871                                DayOfYear(335, MonthDay(11, 30)),
10872                                DayOfYear(336, MonthDay(12, 1)),
10873                                DayOfYear(364, MonthDay(12, 29)),
10874                                DayOfYear(365, MonthDay(12, 30)),
10875                                DayOfYear(366, MonthDay(12, 31))];
10876        return result;
10877     }
10878 
10879     void initializeTests()
10880     {
10881         foreach (year; testYearsBC)
10882         {
10883             foreach (md; testMonthDays)
10884                 testDatesBC ~= Date(year, md.month, md.day);
10885         }
10886 
10887         foreach (year; testYearsAD)
10888         {
10889             foreach (md; testMonthDays)
10890                 testDatesAD ~= Date(year, md.month, md.day);
10891         }
10892 
10893         foreach (dt; testDatesBC)
10894         {
10895             foreach (tod; testTODs)
10896                 testDateTimesBC ~= DateTime(dt, tod);
10897         }
10898 
10899         foreach (dt; testDatesAD)
10900         {
10901             foreach (tod; testTODs)
10902                 testDateTimesAD ~= DateTime(dt, tod);
10903         }
10904     }
10905 }