The OpenD Programming Language

1 // Written in the D programming language
2 /++
3 
4 $(SCRIPT inhibitQuickIndex = 1;)
5 $(DIVC quickindex,
6 $(BOOKTABLE,
7 $(TR $(TH Category) $(TH Functions))
8 $(TR $(TD Main date types) $(TD
9     $(LREF Date)
10     $(LREF DateTime)
11 ))
12 $(TR $(TD Other date types) $(TD
13     $(LREF Month)
14     $(LREF DayOfWeek)
15     $(LREF TimeOfDay)
16 ))
17 $(TR $(TD Date checking) $(TD
18     $(LREF valid)
19     $(LREF validTimeUnits)
20     $(LREF yearIsLeapYear)
21     $(LREF isTimePoint)
22     $(LREF enforceValid)
23 ))
24 $(TR $(TD Date conversion) $(TD
25     $(LREF daysToDayOfWeek)
26     $(LREF monthsToMonth)
27 ))
28 $(TR $(TD Time units) $(TD
29     $(LREF cmpTimeUnits)
30     $(LREF timeStrings)
31 ))
32 $(TR $(TD Other) $(TD
33     $(LREF AllowDayOverflow)
34     $(LREF DateTimeException)
35 ))
36 ))
37 
38     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
39     Authors:   $(HTTP jmdavisprog.com, Jonathan M Davis)
40     Source:    $(PHOBOSSRC std/datetime/date.d)
41 +/
42 module std.datetime.date;
43 
44 import core.time : TimeException;
45 import std.traits : isSomeString, Unqual;
46 import std.typecons : Flag;
47 import std.range.primitives : isOutputRange;
48 
49 version (StdUnittest) import std.exception : assertThrown;
50 
51 @safe unittest
52 {
53     initializeTests();
54 }
55 
56 
57 /++
58     Exception type used by std.datetime. It's an alias to
59     $(REF TimeException,core,time). Either can be caught without concern about
60     which module it came from.
61   +/
62 alias DateTimeException = TimeException;
63 
64 
65 /++
66     Represents the 12 months of the Gregorian year (January is 1).
67   +/
68 enum Month : ubyte
69 {
70     jan = 1, ///
71     feb,     ///
72     mar,     ///
73     apr,     ///
74     may,     ///
75     jun,     ///
76     jul,     ///
77     aug,     ///
78     sep,     ///
79     oct,     ///
80     nov,     ///
81     dec      ///
82 }
83 
84 ///
85 @safe pure unittest
86 {
87     assert(Date(2018, 10, 1).month == Month.oct);
88     assert(DateTime(1, 1, 1).month == Month.jan);
89 }
90 
91 
92 /++
93     Represents the 7 days of the Gregorian week (Sunday is 0).
94   +/
95 enum DayOfWeek : ubyte
96 {
97     sun = 0, ///
98     mon,     ///
99     tue,     ///
100     wed,     ///
101     thu,     ///
102     fri,     ///
103     sat      ///
104 }
105 
106 ///
107 @safe pure unittest
108 {
109     assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
110     assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
111 }
112 
113 /++
114     In some date calculations, adding months or years can cause the date to fall
115     on a day of the month which is not valid (e.g. February 29th 2001 or
116     June 31st 2000). If overflow is allowed (as is the default), then the month
117     will be incremented accordingly (so, February 29th 2001 would become
118     March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
119     is not allowed, then the day will be adjusted to the last valid day in that
120     month (so, February 29th 2001 would become February 28th 2001 and
121     June 31st 2000 would become June 30th 2000).
122 
123     AllowDayOverflow only applies to calculations involving months or years.
124 
125     If set to `AllowDayOverflow.no`, then day overflow is not allowed.
126 
127     Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
128     allowed.
129   +/
130 alias AllowDayOverflow = Flag!"allowDayOverflow";
131 
132 
133 /++
134     Array of the strings representing time units, starting with the smallest
135     unit and going to the largest. It does not include `"nsecs"`.
136 
137     Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
138     `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
139     `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
140     `"years"`
141   +/
142 immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
143                                   "hours", "days", "weeks", "months", "years"];
144 
145 
146 /++
147     Combines the $(REF Date,std,datetime,date) and
148     $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
149     both the date and the time. It is optimized for calendar-based operations
150     and has no concept of time zone. For an object which is optimized for time
151     operations based on the system time, use
152     $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
153     a concept of time zone and has much higher precision (hnsecs). `DateTime`
154     is intended primarily for calendar-based uses rather than precise time
155     operations.
156   +/
157 struct DateTime
158 {
159 public:
160 
161     /++
162         Params:
163             date = The date portion of $(LREF DateTime).
164             tod  = The time portion of $(LREF DateTime).
165       +/
166     this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
167     {
168         _date = date;
169         _tod = tod;
170     }
171 
172     @safe unittest
173     {
174         {
175             auto dt = DateTime.init;
176             assert(dt._date == Date.init);
177             assert(dt._tod == TimeOfDay.init);
178         }
179 
180         {
181             auto dt = DateTime(Date(1999, 7 ,6));
182             assert(dt._date == Date(1999, 7, 6));
183             assert(dt._tod == TimeOfDay.init);
184         }
185 
186         {
187             auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33));
188             assert(dt._date == Date(1999, 7, 6));
189             assert(dt._tod == TimeOfDay(12, 30, 33));
190         }
191     }
192 
193 
194     /++
195         Params:
196             year   = The year portion of the date.
197             month  = The month portion of the date (January is 1).
198             day    = The day portion of the date.
199             hour   = The hour portion of the time;
200             minute = The minute portion of the time;
201             second = The second portion of the time;
202       +/
203     this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure
204     {
205         _date = Date(year, month, day);
206         _tod = TimeOfDay(hour, minute, second);
207     }
208 
209     @safe unittest
210     {
211         {
212             auto dt = DateTime(1999, 7 ,6);
213             assert(dt._date == Date(1999, 7, 6));
214             assert(dt._tod == TimeOfDay.init);
215         }
216 
217         {
218             auto dt = DateTime(1999, 7 ,6, 12, 30, 33);
219             assert(dt._date == Date(1999, 7, 6));
220             assert(dt._tod == TimeOfDay(12, 30, 33));
221         }
222     }
223 
224 
225     /++
226         Compares this $(LREF DateTime) with the given `DateTime.`.
227 
228         Returns:
229             $(BOOKTABLE,
230             $(TR $(TD this < rhs) $(TD < 0))
231             $(TR $(TD this == rhs) $(TD 0))
232             $(TR $(TD this > rhs) $(TD > 0))
233             )
234      +/
235     int opCmp(DateTime rhs) const @safe pure nothrow @nogc
236     {
237         immutable dateResult = _date.opCmp(rhs._date);
238 
239         if (dateResult != 0)
240             return dateResult;
241 
242         return _tod.opCmp(rhs._tod);
243     }
244 
245     @safe unittest
246     {
247         // Test A.D.
248         assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0);
249 
250         assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0);
251         assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0);
252         assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0);
253 
254         assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0);
255         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0);
256 
257         assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0);
258 
259         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
260         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
261         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
262         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
263         assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0);
264         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
265 
266         assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
267         assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
268         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
269         assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
270         assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
271         assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
272 
273 
274         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp(
275                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0);
276         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp(
277                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0);
278         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp(
279                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0);
280         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
281                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
282 
283         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp(
284                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0);
285         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
286                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
287 
288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp(
289                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0);
290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
291                    DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
292 
293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
294                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
296                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
298                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
300                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
302                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0);
303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
304                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
305 
306         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
307                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
308         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
309                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
310         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
311                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
312         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
313                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
314 
315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
316                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
317         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
318                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
319 
320         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
321                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
322         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
323                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
324         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
325                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
326         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
327                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
328         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
329                    DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
330         assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
331                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
332 
333         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
334                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
335         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
336                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
337         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
338                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
339         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
340                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
341         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
342                    DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
343         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
344                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
345 
346         assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
347                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
348         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
349                    DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
350         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
351                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0);
352         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
353                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
354         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
355                    DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
356         assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
357                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
358 
359         // Test B.C.
360         assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp(
361                    DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0);
362         assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
363                    DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0);
364         assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp(
365                    DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0);
366 
367         assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
368                    DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0);
369         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
370                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
371 
372         assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
373                    DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0);
374 
375         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
376                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
377         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
378                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
379         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
380                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
381         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
382                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
383         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
384                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
385         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
386                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
387 
388         assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
389                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
390         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
391                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
392         assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
393                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
394         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
395                    DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
396         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
397                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
398         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
399                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
400 
401         // Test Both
402         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
403                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
404         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
405                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
406 
407         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
408                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
409         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
410                    DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0);
411 
412         assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
413                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
414         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
415                    DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
416 
417         assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
418                    DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
419         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
420                    DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0);
421 
422         assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
423                    DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0);
424         assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp(
425                    DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
426 
427         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
428         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
429         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
430         assert(dt.opCmp(dt) == 0);
431         assert(dt.opCmp(cdt) == 0);
432         assert(dt.opCmp(idt) == 0);
433         assert(cdt.opCmp(dt) == 0);
434         assert(cdt.opCmp(cdt) == 0);
435         assert(cdt.opCmp(idt) == 0);
436         assert(idt.opCmp(dt) == 0);
437         assert(idt.opCmp(cdt) == 0);
438         assert(idt.opCmp(idt) == 0);
439     }
440 
441 
442     /++
443         The date portion of $(LREF DateTime).
444       +/
445     @property Date date() const @safe pure nothrow @nogc
446     {
447         return _date;
448     }
449 
450     @safe unittest
451     {
452         {
453             auto dt = DateTime.init;
454             assert(dt.date == Date.init);
455         }
456 
457         {
458             auto dt = DateTime(Date(1999, 7, 6));
459             assert(dt.date == Date(1999, 7, 6));
460         }
461 
462         const cdt = DateTime(1999, 7, 6);
463         immutable idt = DateTime(1999, 7, 6);
464         assert(cdt.date == Date(1999, 7, 6));
465         assert(idt.date == Date(1999, 7, 6));
466     }
467 
468 
469     /++
470         The date portion of $(LREF DateTime).
471 
472         Params:
473             date = The Date to set this $(LREF DateTime)'s date portion to.
474       +/
475     @property void date(Date date) @safe pure nothrow @nogc
476     {
477         _date = date;
478     }
479 
480     @safe unittest
481     {
482         auto dt = DateTime.init;
483         dt.date = Date(1999, 7, 6);
484         assert(dt._date == Date(1999, 7, 6));
485         assert(dt._tod == TimeOfDay.init);
486 
487         const cdt = DateTime(1999, 7, 6);
488         immutable idt = DateTime(1999, 7, 6);
489         static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1)));
490         static assert(!__traits(compiles, idt.date = Date(2010, 1, 1)));
491     }
492 
493 
494     /++
495         The time portion of $(LREF DateTime).
496       +/
497     @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
498     {
499         return _tod;
500     }
501 
502     @safe unittest
503     {
504         {
505             auto dt = DateTime.init;
506             assert(dt.timeOfDay == TimeOfDay.init);
507         }
508 
509         {
510             auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33));
511             assert(dt.timeOfDay == TimeOfDay(12, 30, 33));
512         }
513 
514         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
515         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
516         assert(cdt.timeOfDay == TimeOfDay(12, 30, 33));
517         assert(idt.timeOfDay == TimeOfDay(12, 30, 33));
518     }
519 
520 
521     /++
522         The time portion of $(LREF DateTime).
523 
524         Params:
525             tod = The $(REF TimeOfDay,std,datetime,date) to set this
526                   $(LREF DateTime)'s time portion to.
527       +/
528     @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
529     {
530         _tod = tod;
531     }
532 
533     @safe unittest
534     {
535         auto dt = DateTime.init;
536         dt.timeOfDay = TimeOfDay(12, 30, 33);
537         assert(dt._date == Date.init);
538         assert(dt._tod == TimeOfDay(12, 30, 33));
539 
540         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
541         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
542         static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33)));
543         static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33)));
544     }
545 
546 
547     /++
548         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
549         are B.C.
550      +/
551     @property short year() const @safe pure nothrow @nogc
552     {
553         return _date.year;
554     }
555 
556     @safe unittest
557     {
558         assert(Date.init.year == 1);
559         assert(Date(1999, 7, 6).year == 1999);
560         assert(Date(-1999, 7, 6).year == -1999);
561 
562         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
563         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
564         assert(idt.year == 1999);
565         assert(idt.year == 1999);
566     }
567 
568 
569     /++
570         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
571         are B.C.
572 
573         Params:
574             year = The year to set this $(LREF DateTime)'s year to.
575 
576         Throws:
577             $(REF DateTimeException,std,datetime,date) if the new year is not
578             a leap year and if the resulting date would be on February 29th.
579      +/
580     @property void year(int year) @safe pure
581     {
582         _date.year = year;
583     }
584 
585     ///
586     @safe unittest
587     {
588         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
589         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
590         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
591     }
592 
593     @safe unittest
594     {
595         static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
596         {
597             dt.year = year;
598             assert(dt == expected);
599         }
600 
601         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
602                1999,
603                DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)));
604         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
605                0,
606                DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)));
607         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
608                -1999,
609                DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)));
610 
611         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
612         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
613         static assert(!__traits(compiles, cdt.year = 7));
614         static assert(!__traits(compiles, idt.year = 7));
615     }
616 
617 
618     /++
619         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
620 
621         Throws:
622             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
623      +/
624     @property short yearBC() const @safe pure
625     {
626         return _date.yearBC;
627     }
628 
629     ///
630     @safe unittest
631     {
632         assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
633         assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
634         assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
635     }
636 
637     @safe unittest
638     {
639         assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
640 
641         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
642         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
643         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
644         dt.yearBC = 12;
645         assert(dt.yearBC == 12);
646         static assert(!__traits(compiles, cdt.yearBC = 12));
647         static assert(!__traits(compiles, idt.yearBC = 12));
648     }
649 
650 
651     /++
652         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
653 
654         Params:
655             year = The year B.C. to set this $(LREF DateTime)'s year to.
656 
657         Throws:
658             $(REF DateTimeException,std,datetime,date) if a non-positive value
659             is given.
660      +/
661     @property void yearBC(int year) @safe pure
662     {
663         _date.yearBC = year;
664     }
665 
666     ///
667     @safe unittest
668     {
669         auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
670         dt.yearBC = 1;
671         assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
672 
673         dt.yearBC = 10;
674         assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
675     }
676 
677     @safe unittest
678     {
679         assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1))));
680 
681         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
682         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
683         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
684         dt.yearBC = 12;
685         assert(dt.yearBC == 12);
686         static assert(!__traits(compiles, cdt.yearBC = 12));
687         static assert(!__traits(compiles, idt.yearBC = 12));
688     }
689 
690 
691     /++
692         Month of a Gregorian Year.
693      +/
694     @property Month month() const @safe pure nothrow @nogc
695     {
696         return _date.month;
697     }
698 
699     ///
700     @safe unittest
701     {
702         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
703         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
704         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
705     }
706 
707     @safe unittest
708     {
709         assert(DateTime.init.month == 1);
710         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
711         assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
712 
713         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
714         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
715         assert(cdt.month == 7);
716         assert(idt.month == 7);
717     }
718 
719 
720     /++
721         Month of a Gregorian Year.
722 
723         Params:
724             month = The month to set this $(LREF DateTime)'s month to.
725 
726         Throws:
727             $(REF DateTimeException,std,datetime,date) if the given month is
728             not a valid month.
729      +/
730     @property void month(Month month) @safe pure
731     {
732         _date.month = month;
733     }
734 
735     @safe unittest
736     {
737         static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
738         {
739             dt.month = month;
740             assert(expected != DateTime.init);
741             assert(dt == expected);
742         }
743 
744         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0));
745         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13));
746 
747         testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
748                cast(Month) 7,
749                DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)));
750         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)),
751                cast(Month) 7,
752                DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)));
753 
754         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
755         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
756         static assert(!__traits(compiles, cdt.month = 12));
757         static assert(!__traits(compiles, idt.month = 12));
758     }
759 
760 
761     /++
762         Day of a Gregorian Month.
763      +/
764     @property ubyte day() const @safe pure nothrow @nogc
765     {
766         return _date.day;
767     }
768 
769     ///
770     @safe unittest
771     {
772         assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
773         assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
774         assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
775     }
776 
777     @safe unittest
778     {
779         import std.format : format;
780         import std.range : chain;
781 
782         static void test(DateTime dateTime, int expected)
783         {
784             assert(dateTime.day == expected, format("Value given: %s", dateTime));
785         }
786 
787         foreach (year; chain(testYearsBC, testYearsAD))
788         {
789             foreach (md; testMonthDays)
790             {
791                 foreach (tod; testTODs)
792                     test(DateTime(Date(year, md.month, md.day), tod), md.day);
793             }
794         }
795 
796         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
797         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
798         assert(cdt.day == 6);
799         assert(idt.day == 6);
800     }
801 
802 
803     /++
804         Day of a Gregorian Month.
805 
806         Params:
807             day = The day of the month to set this $(LREF DateTime)'s day to.
808 
809         Throws:
810             $(REF DateTimeException,std,datetime,date) if the given day is not
811             a valid day of the current month.
812      +/
813     @property void day(int day) @safe pure
814     {
815         _date.day = day;
816     }
817 
818     @safe unittest
819     {
820         import std.exception : assertNotThrown;
821 
822         static void testDT(DateTime dt, int day)
823         {
824             dt.day = day;
825         }
826 
827         // Test A.D.
828         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0));
829         assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32));
830         assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29));
831         assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30));
832         assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32));
833         assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31));
834         assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32));
835         assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31));
836         assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32));
837         assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32));
838         assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31));
839         assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32));
840         assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31));
841         assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32));
842 
843         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31));
844         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28));
845         assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29));
846         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31));
847         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30));
848         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31));
849         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30));
850         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31));
851         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31));
852         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30));
853         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31));
854         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30));
855         assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31));
856 
857         {
858             auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22));
859             dt.day = 6;
860             assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22)));
861         }
862 
863         // Test B.C.
864         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0));
865         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32));
866         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29));
867         assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30));
868         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32));
869         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31));
870         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32));
871         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31));
872         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32));
873         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32));
874         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31));
875         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32));
876         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31));
877         assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32));
878 
879         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31));
880         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28));
881         assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29));
882         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31));
883         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30));
884         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31));
885         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30));
886         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31));
887         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31));
888         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30));
889         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31));
890         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30));
891         assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31));
892 
893         auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22));
894         dt.day = 6;
895         assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22)));
896 
897         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
898         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
899         static assert(!__traits(compiles, cdt.day = 27));
900         static assert(!__traits(compiles, idt.day = 27));
901     }
902 
903 
904     /++
905         Hours past midnight.
906      +/
907     @property ubyte hour() const @safe pure nothrow @nogc
908     {
909         return _tod.hour;
910     }
911 
912     @safe unittest
913     {
914         assert(DateTime.init.hour == 0);
915         assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12);
916 
917         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
918         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
919         assert(cdt.hour == 12);
920         assert(idt.hour == 12);
921     }
922 
923 
924     /++
925         Hours past midnight.
926 
927         Params:
928             hour = The hour of the day to set this $(LREF DateTime)'s hour to.
929 
930         Throws:
931             $(REF DateTimeException,std,datetime,date) if the given hour would
932             result in an invalid $(LREF DateTime).
933      +/
934     @property void hour(int hour) @safe pure
935     {
936         _tod.hour = hour;
937     }
938 
939     @safe unittest
940     {
941         assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}());
942 
943         auto dt = DateTime.init;
944         dt.hour = 12;
945         assert(dt == DateTime(1, 1, 1, 12, 0, 0));
946 
947         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
948         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
949         static assert(!__traits(compiles, cdt.hour = 27));
950         static assert(!__traits(compiles, idt.hour = 27));
951     }
952 
953 
954     /++
955         Minutes past the hour.
956      +/
957     @property ubyte minute() const @safe pure nothrow @nogc
958     {
959         return _tod.minute;
960     }
961 
962     @safe unittest
963     {
964         assert(DateTime.init.minute == 0);
965         assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30);
966 
967         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
968         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
969         assert(cdt.minute == 30);
970         assert(idt.minute == 30);
971     }
972 
973 
974     /++
975         Minutes past the hour.
976 
977         Params:
978             minute = The minute to set this $(LREF DateTime)'s minute to.
979 
980         Throws:
981             $(REF DateTimeException,std,datetime,date) if the given minute
982             would result in an invalid $(LREF DateTime).
983      +/
984     @property void minute(int minute) @safe pure
985     {
986         _tod.minute = minute;
987     }
988 
989     @safe unittest
990     {
991         assertThrown!DateTimeException((){DateTime.init.minute = 60;}());
992 
993         auto dt = DateTime.init;
994         dt.minute = 30;
995         assert(dt == DateTime(1, 1, 1, 0, 30, 0));
996 
997         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
998         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
999         static assert(!__traits(compiles, cdt.minute = 27));
1000         static assert(!__traits(compiles, idt.minute = 27));
1001     }
1002 
1003 
1004     /++
1005         Seconds past the minute.
1006      +/
1007     @property ubyte second() const @safe pure nothrow @nogc
1008     {
1009         return _tod.second;
1010     }
1011 
1012     @safe unittest
1013     {
1014         assert(DateTime.init.second == 0);
1015         assert(DateTime(1, 1, 1, 0, 0, 33).second == 33);
1016 
1017         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1018         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1019         assert(cdt.second == 33);
1020         assert(idt.second == 33);
1021     }
1022 
1023 
1024     /++
1025         Seconds past the minute.
1026 
1027         Params:
1028             second = The second to set this $(LREF DateTime)'s second to.
1029 
1030         Throws:
1031             $(REF DateTimeException,std,datetime,date) if the given seconds
1032             would result in an invalid $(LREF DateTime).
1033      +/
1034     @property void second(int second) @safe pure
1035     {
1036         _tod.second = second;
1037     }
1038 
1039     @safe unittest
1040     {
1041         assertThrown!DateTimeException((){DateTime.init.second = 60;}());
1042 
1043         auto dt = DateTime.init;
1044         dt.second = 33;
1045         assert(dt == DateTime(1, 1, 1, 0, 0, 33));
1046 
1047         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1048         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1049         static assert(!__traits(compiles, cdt.second = 27));
1050         static assert(!__traits(compiles, idt.second = 27));
1051     }
1052 
1053 
1054     /++
1055         Adds the given number of years or months to this $(LREF DateTime),
1056         mutating it. A negative number will subtract.
1057 
1058         Note that if day overflow is allowed, and the date with the adjusted
1059         year/month overflows the number of days in the new month, then the month
1060         will be incremented by one, and the day set to the number of days
1061         overflowed. (e.g. if the day were 31 and the new month were June, then
1062         the month would be incremented to July, and the new day would be 1). If
1063         day overflow is not allowed, then the day will be set to the last valid
1064         day in the month (e.g. June 31st would become June 30th).
1065 
1066         Params:
1067             units         = The type of units to add ("years" or "months").
1068             value         = The number of months or years to add to this
1069                             $(LREF DateTime).
1070             allowOverflow = Whether the days should be allowed to overflow,
1071                             causing the month to increment.
1072 
1073         Returns:
1074             A reference to the `DateTime` (`this`).
1075       +/
1076     ref DateTime add(string units)
1077                     (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1078         if (units == "years" || units == "months")
1079     {
1080         _date.add!units(value, allowOverflow);
1081         return this;
1082     }
1083 
1084     ///
1085     @safe unittest
1086     {
1087         auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
1088         dt1.add!"months"(11);
1089         assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
1090 
1091         auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
1092         dt2.add!"months"(-11);
1093         assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
1094 
1095         auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
1096         dt3.add!"years"(1);
1097         assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
1098 
1099         auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
1100         dt4.add!"years"(1, AllowDayOverflow.no);
1101         assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
1102     }
1103 
1104     @safe unittest
1105     {
1106         auto dt = DateTime(2000, 1, 31);
1107         dt.add!"years"(7).add!"months"(-4);
1108         assert(dt == DateTime(2006, 10, 1));
1109 
1110         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1111         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1112         static assert(!__traits(compiles, cdt.add!"years"(4)));
1113         static assert(!__traits(compiles, idt.add!"years"(4)));
1114         static assert(!__traits(compiles, cdt.add!"months"(4)));
1115         static assert(!__traits(compiles, idt.add!"months"(4)));
1116     }
1117 
1118 
1119     /++
1120         Adds the given number of years or months to this $(LREF DateTime),
1121         mutating it. A negative number will subtract.
1122 
1123         The difference between rolling and adding is that rolling does not
1124         affect larger units. Rolling a $(LREF DateTime) 12 months
1125         gets the exact same $(LREF DateTime). However, the days can still be
1126         affected due to the differing number of days in each month.
1127 
1128         Because there are no units larger than years, there is no difference
1129         between adding and rolling years.
1130 
1131         Params:
1132             units         = The type of units to add ("years" or "months").
1133             value         = The number of months or years to add to this
1134                             $(LREF DateTime).
1135             allowOverflow = Whether the days should be allowed to overflow,
1136                             causing the month to increment.
1137 
1138         Returns:
1139             A reference to the `DateTime` (`this`).
1140       +/
1141     ref DateTime roll(string units)
1142                      (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1143         if (units == "years" || units == "months")
1144     {
1145         _date.roll!units(value, allowOverflow);
1146         return this;
1147     }
1148 
1149     ///
1150     @safe unittest
1151     {
1152         auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
1153         dt1.roll!"months"(1);
1154         assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
1155 
1156         auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
1157         dt2.roll!"months"(-1);
1158         assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
1159 
1160         auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
1161         dt3.roll!"months"(1);
1162         assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
1163 
1164         auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
1165         dt4.roll!"months"(1, AllowDayOverflow.no);
1166         assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
1167 
1168         auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
1169         dt5.roll!"years"(1);
1170         assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
1171 
1172         auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
1173         dt6.roll!"years"(1, AllowDayOverflow.no);
1174         assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
1175     }
1176 
1177     @safe unittest
1178     {
1179         auto dt = DateTime(2000, 1, 31);
1180         dt.roll!"years"(7).roll!"months"(-4);
1181         assert(dt == DateTime(2007, 10, 1));
1182 
1183         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1184         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1185         static assert(!__traits(compiles, cdt.roll!"years"(4)));
1186         static assert(!__traits(compiles, idt.roll!"years"(4)));
1187         static assert(!__traits(compiles, cdt.roll!"months"(4)));
1188         static assert(!__traits(compiles, idt.roll!"months"(4)));
1189     }
1190 
1191 
1192     /++
1193         Adds the given number of units to this $(LREF DateTime), mutating it. A
1194         negative number will subtract.
1195 
1196         The difference between rolling and adding is that rolling does not
1197         affect larger units. For instance, rolling a $(LREF DateTime) one
1198         year's worth of days gets the exact same $(LREF DateTime).
1199 
1200         Accepted units are `"days"`, `"minutes"`, `"hours"`,
1201         `"minutes"`, and `"seconds"`.
1202 
1203         Params:
1204             units = The units to add.
1205             value = The number of $(D_PARAM units) to add to this
1206                     $(LREF DateTime).
1207 
1208         Returns:
1209             A reference to the `DateTime` (`this`).
1210       +/
1211     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1212         if (units == "days")
1213     {
1214         _date.roll!"days"(value);
1215         return this;
1216     }
1217 
1218     ///
1219     @safe unittest
1220     {
1221         auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
1222         dt1.roll!"days"(1);
1223         assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
1224         dt1.roll!"days"(365);
1225         assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
1226         dt1.roll!"days"(-32);
1227         assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
1228 
1229         auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
1230         dt2.roll!"hours"(1);
1231         assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
1232 
1233         auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
1234         dt3.roll!"seconds"(-1);
1235         assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
1236     }
1237 
1238     @safe unittest
1239     {
1240         auto dt = DateTime(2000, 1, 31);
1241         dt.roll!"days"(7).roll!"days"(-4);
1242         assert(dt == DateTime(2000, 1, 3));
1243 
1244         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1245         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1246         static assert(!__traits(compiles, cdt.roll!"days"(4)));
1247         static assert(!__traits(compiles, idt.roll!"days"(4)));
1248     }
1249 
1250 
1251     /// ditto
1252     ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1253         if (units == "hours" ||
1254             units == "minutes" ||
1255             units == "seconds")
1256     {
1257         _tod.roll!units(value);
1258         return this;
1259     }
1260 
1261     // Test roll!"hours"().
1262     @safe unittest
1263     {
1264         static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
1265         {
1266             orig.roll!"hours"(hours);
1267             assert(orig == expected);
1268         }
1269 
1270         // Test A.D.
1271         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1273         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1274                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1275         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1276                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1277         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1278                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1279         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1280                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1281         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1282                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1283         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1284                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1285         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1286                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1287         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1288                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1289         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1290                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1291         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1292                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1293         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1294                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1295         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1296                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1297         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1298                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1299         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1300                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1301         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1302                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1303         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1304                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1305         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1306                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1307         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1308                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1309         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1310                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1311         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1312                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1313         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1314                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1315         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1316                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1317         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1318                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1319         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1320                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1321         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1322                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1323 
1324         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1325                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1326         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1327                DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1328         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1329                DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1330         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1331                DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1332         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1333                DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1334         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1335                DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1336         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1337                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1338         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1339                DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1340         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1341                DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1342         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1343                DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1344         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1345                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1346         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1347                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1348         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1349                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1350         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1351                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1352         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1353                DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1354         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1355                DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1356         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1357                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1358         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1359                DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1360         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1361                DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1362         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1363                DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1364         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1365                DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1366         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1367                DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1368         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1369                DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1370         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1371                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1372         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1373                DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1374 
1375         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1376                DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1377         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1378                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1379         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1380                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1381 
1382         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1383                DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1384         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1385                DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1386         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1387                DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1388 
1389         testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1390                DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33)));
1391         testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1392                DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33)));
1393 
1394         testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1,
1395                DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33)));
1396         testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1397                DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33)));
1398 
1399         testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25,
1400                DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33)));
1401         testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25,
1402                DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33)));
1403 
1404         testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1405                DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33)));
1406         testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1407                DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33)));
1408 
1409         // Test B.C.
1410         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1411                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1412         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1413                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1414         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1415                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1416         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1417                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1418         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1419                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1420         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1421                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1422         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1423                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1424         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1425                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1426         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1427                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1428         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1429                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1430         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1431                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1432         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1433                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1434         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1435                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1436         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1437                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1438         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1439                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1440         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1441                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1442         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1443                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1444         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1445                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1446         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1447                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1448         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1449                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1450         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1451                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1452         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1453                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1454         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1455                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1456         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1457                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1458         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1459                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1460         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1461                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1462 
1463         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1464                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1465         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1466                DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1467         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1468                DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1469         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1470                DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1471         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1472                DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1473         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1474                DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1475         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1476                DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1477         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1478                DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1479         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1480                DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1481         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1482                DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1483         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1484                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1485         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1486                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1487         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1488                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1489         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1490                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1491         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1492                DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1493         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1494                DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1495         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1496                DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1497         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1498                DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1499         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1500                DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1501         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1502                DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1503         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1504                DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1505         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1506                DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1507         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1508                DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1509         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1510                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1511         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1512                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1513 
1514         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1515                DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1516         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1517                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1518         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1519                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1520 
1521         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1522                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1523         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1524                DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1525         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1526                DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1527 
1528         testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1529                DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33)));
1530         testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1531                DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33)));
1532 
1533         testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1,
1534                DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33)));
1535         testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1536                DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33)));
1537 
1538         testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25,
1539                DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33)));
1540         testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25,
1541                DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33)));
1542 
1543         testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1544                DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33)));
1545         testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1546                DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33)));
1547 
1548         // Test Both
1549         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546,
1550                DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33)));
1551         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546,
1552                DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33)));
1553 
1554         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1555         dt.roll!"hours"(27).roll!"hours"(-9);
1556         assert(dt == DateTime(2000, 1, 31, 3, 7, 6));
1557 
1558         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1559         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1560         static assert(!__traits(compiles, cdt.roll!"hours"(4)));
1561         static assert(!__traits(compiles, idt.roll!"hours"(4)));
1562     }
1563 
1564     // Test roll!"minutes"().
1565     @safe unittest
1566     {
1567         static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
1568         {
1569             orig.roll!"minutes"(minutes);
1570             assert(orig == expected);
1571         }
1572 
1573         // Test A.D.
1574         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1575                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1576         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1577                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1578         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1579                DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33)));
1580         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1581                DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33)));
1582         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1583                DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33)));
1584         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1585                DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33)));
1586         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1587                DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33)));
1588         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1589                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1590         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1591                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1592         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1593                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1594         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1595                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1596         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1597                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1598         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1599                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1600         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1601                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1602         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1603                DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33)));
1604 
1605         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1606                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1607         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1608                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1609         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1610                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1611         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1612                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1613         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1614                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1615         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1616                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1617         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1618                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1619         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1620                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1621 
1622         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1623                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1624         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1625                DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33)));
1626         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1627                DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33)));
1628         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1629                DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33)));
1630         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1631                DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33)));
1632         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1633                DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33)));
1634         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1635                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1636         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1637                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1638         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1639                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1640         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1641                DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1642         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1643                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1644         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1645                DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1646         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1647                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1648         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1649                DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33)));
1650 
1651         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1652                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1653         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1654                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1655         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1656                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1657         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1658                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1659         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1660                DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1661         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1662                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1663         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1664                DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1665         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1666                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1667 
1668         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1669                DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1670         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1671                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1672         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1673                DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1674 
1675         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1676                DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33)));
1677         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1678                DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)));
1679         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1680                DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33)));
1681 
1682         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1683                DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33)));
1684         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1685                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)));
1686         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1687                DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33)));
1688 
1689         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1690                DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33)));
1691         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1692                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)));
1693         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1694                DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33)));
1695 
1696         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1,
1697                DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33)));
1698         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0,
1699                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)));
1700         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1,
1701                DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33)));
1702 
1703         // Test B.C.
1704         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1705                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1706         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1707                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1708         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1709                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33)));
1710         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1711                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33)));
1712         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1713                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33)));
1714         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1715                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33)));
1716         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1717                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33)));
1718         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1719                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1720         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1721                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1722         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1723                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1724         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1725                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1726         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1727                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1728         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1729                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1730         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1731                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1732         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1733                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33)));
1734 
1735         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1736                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1737         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1738                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1739         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1740                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1741         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1742                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1743         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1744                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1745         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1746                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1747         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1748                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1749         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1750                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1751 
1752         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1753                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1754         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1755                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33)));
1756         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1757                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33)));
1758         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1759                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33)));
1760         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1761                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33)));
1762         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1763                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33)));
1764         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1765                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1766         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1767                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1768         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1769                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1770         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1771                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1772         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1773                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1774         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1775                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1776         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1777                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1778         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1779                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33)));
1780 
1781         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1782                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1783         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1784                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1785         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1786                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1787         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1788                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1789         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1790                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1791         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1792                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1793         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1794                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1795         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1796                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1797 
1798         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1799                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1800         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1801                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1802         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1803                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1804 
1805         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1806                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33)));
1807         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1808                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)));
1809         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1810                DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33)));
1811 
1812         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1813                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33)));
1814         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1815                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)));
1816         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1817                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33)));
1818 
1819         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1820                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33)));
1821         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1822                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)));
1823         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1824                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33)));
1825 
1826         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1,
1827                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33)));
1828         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0,
1829                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)));
1830         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1,
1831                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33)));
1832 
1833         // Test Both
1834         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
1835                DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0)));
1836         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1,
1837                DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0)));
1838 
1839         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
1840                DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0)));
1841         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1,
1842                DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0)));
1843 
1844         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760,
1845                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
1846         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760,
1847                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1848 
1849         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782,
1850                DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33)));
1851         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782,
1852                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1853 
1854         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1855         dt.roll!"minutes"(92).roll!"minutes"(-292);
1856         assert(dt == DateTime(2000, 1, 31, 9, 47, 6));
1857 
1858         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1859         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1860         static assert(!__traits(compiles, cdt.roll!"minutes"(4)));
1861         static assert(!__traits(compiles, idt.roll!"minutes"(4)));
1862     }
1863 
1864     // Test roll!"seconds"().
1865     @safe unittest
1866     {
1867         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
1868         {
1869             orig.roll!"seconds"(seconds);
1870             assert(orig == expected);
1871         }
1872 
1873         // Test A.D.
1874         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1875                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1876         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1877                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1878         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1879                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35)));
1880         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1881                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36)));
1882         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1883                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37)));
1884         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1885                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38)));
1886         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1887                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43)));
1888         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1889                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48)));
1890         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
1891                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1892         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
1893                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1894         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1895                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3)));
1896         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
1897                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1898         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1899                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1900         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
1901                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1902 
1903         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
1904                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1905         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
1906                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1907         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
1908                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1909         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
1910                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1911         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
1912                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1913         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
1914                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1915         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
1916                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1917         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
1918                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1919 
1920         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1921                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1922         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1923                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31)));
1924         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1925                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30)));
1926         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1927                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29)));
1928         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1929                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28)));
1930         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1931                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23)));
1932         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1933                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18)));
1934         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
1935                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1936         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
1937                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1938         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
1939                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58)));
1940         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
1941                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1942         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1943                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1944         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
1945                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1946 
1947         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
1948                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1949         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
1950                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1951         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
1952                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1953 
1954         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
1955                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1)));
1956         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
1957                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)));
1958         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
1959                DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59)));
1960 
1961         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
1962                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1)));
1963         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
1964                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)));
1965         testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
1966                DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59)));
1967 
1968         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
1969                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0)));
1970         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
1971                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)));
1972         testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
1973                DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58)));
1974 
1975         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1,
1976                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0)));
1977         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0,
1978                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)));
1979         testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1,
1980                DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58)));
1981 
1982         // Test B.C.
1983         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1984                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1985         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1986                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1987         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1988                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35)));
1989         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1990                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36)));
1991         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1992                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37)));
1993         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1994                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38)));
1995         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1996                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43)));
1997         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1998                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48)));
1999         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
2000                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2001         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
2002                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2003         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
2004                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3)));
2005         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
2006                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2007         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
2008                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2009         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
2010                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2011 
2012         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
2013                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2014         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
2015                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2016         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
2017                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2018         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
2019                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2020         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
2021                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2022         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
2023                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2024         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
2025                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2026         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
2027                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2028 
2029         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
2030                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2031         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
2032                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31)));
2033         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
2034                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30)));
2035         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
2036                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29)));
2037         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
2038                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28)));
2039         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
2040                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23)));
2041         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
2042                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18)));
2043         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
2044                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2045         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
2046                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2047         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
2048                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58)));
2049         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
2050                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2051         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
2052                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2053         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
2054                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2055 
2056         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
2057                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2058         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
2059                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2060         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
2061                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2062 
2063         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
2064                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1)));
2065         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
2066                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)));
2067         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
2068                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59)));
2069 
2070         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
2071                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1)));
2072         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
2073                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)));
2074         testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
2075                DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59)));
2076 
2077         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
2078                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0)));
2079         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
2080                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)));
2081         testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
2082                DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58)));
2083 
2084         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1,
2085                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0)));
2086         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0,
2087                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)));
2088         testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1,
2089                DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58)));
2090 
2091         // Test Both
2092         testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
2093                DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59)));
2094         testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1,
2095                DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)));
2096 
2097         testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
2098                DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59)));
2099         testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1,
2100                DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)));
2101 
2102         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L,
2103                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
2104         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L,
2105                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2106 
2107         testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L,
2108                DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50)));
2109         testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L,
2110                DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2111 
2112         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2113         dt.roll!"seconds"(92).roll!"seconds"(-292);
2114         assert(dt == DateTime(2000, 1, 31, 9, 7, 46));
2115 
2116         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2117         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2118         static assert(!__traits(compiles, cdt.roll!"seconds"(4)));
2119         static assert(!__traits(compiles, idt.roll!"seconds"(4)));
2120     }
2121 
2122 
2123     import core.time : Duration;
2124     /++
2125         Gives the result of adding or subtracting a $(REF Duration, core,time)
2126         from this $(LREF DateTime).
2127 
2128         The legal types of arithmetic for $(LREF DateTime) using this operator
2129         are
2130 
2131         $(BOOKTABLE,
2132         $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
2133         $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
2134         )
2135 
2136         Params:
2137             duration = The $(REF Duration, core,time) to add to or subtract from
2138                        this $(LREF DateTime).
2139       +/
2140     DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
2141         if (op == "+" || op == "-")
2142     {
2143         DateTime retval = this;
2144         immutable seconds = duration.total!"seconds";
2145         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
2146     }
2147 
2148     ///
2149     @safe unittest
2150     {
2151         import core.time : hours, seconds;
2152 
2153         assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
2154                DateTime(2016, 1, 1, 0, 0, 0));
2155 
2156         assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
2157                DateTime(2016, 1, 1, 0, 59, 59));
2158 
2159         assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
2160                DateTime(2015, 12, 31, 23, 59, 59));
2161 
2162         assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
2163                DateTime(2015, 12, 31, 23, 59, 59));
2164     }
2165 
2166     @safe unittest
2167     {
2168         import core.time : dur;
2169 
2170         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2171 
2172         assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2173         assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2174         assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2175         assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2176 
2177         assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2178         assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2179         assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2180         assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2181         assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2182         assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2183         assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2184         assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2185         assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2186         assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2187         assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2188         assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2189 
2190         assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2191         assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2192         assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2193         assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2194 
2195         assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2196         assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2197         assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2198         assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2199         assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2200         assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2201         assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2202         assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2203         assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2204         assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2205         assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2206         assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2207 
2208         auto duration = dur!"seconds"(12);
2209         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2210         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2211         assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2212         assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2213         assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2214         assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2215     }
2216 
2217 
2218     /++
2219         Gives the result of adding or subtracting a duration from this
2220         $(LREF DateTime), as well as assigning the result to this
2221         $(LREF DateTime).
2222 
2223         The legal types of arithmetic for $(LREF DateTime) using this operator
2224         are
2225 
2226         $(BOOKTABLE,
2227         $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime))
2228         $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime))
2229         )
2230 
2231         Params:
2232             duration = The duration to add to or subtract from this
2233                        $(LREF DateTime).
2234       +/
2235     ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
2236         if (op == "+" || op == "-")
2237     {
2238         import core.time : convert;
2239         import std.format : format;
2240 
2241         DateTime retval = this;
2242         immutable hnsecs = duration.total!"hnsecs";
2243 
2244         mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
2245     }
2246 
2247     @safe unittest
2248     {
2249         import core.time : dur;
2250         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
2251                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2252         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
2253                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2254         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) ==
2255                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2256         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) ==
2257                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2258 
2259         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) ==
2260                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2261         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) ==
2262                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2263         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) ==
2264                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2265         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) ==
2266                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2267         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) ==
2268                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2269         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) ==
2270                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2271         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) ==
2272                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2273         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) ==
2274                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2275         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) ==
2276                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2277         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) ==
2278                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2279         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) ==
2280                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2281         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) ==
2282                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2283 
2284         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) ==
2285                DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2286         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) ==
2287                DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2288         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) ==
2289                DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2290         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) ==
2291                DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2292 
2293         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) ==
2294                DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2295         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) ==
2296                DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2297         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) ==
2298                DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2299         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) ==
2300                DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2301         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) ==
2302                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2303         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) ==
2304                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2305         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) ==
2306                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2307         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) ==
2308                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2309         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) ==
2310                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2311         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) ==
2312                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2313         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) ==
2314                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2315         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) ==
2316                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2317 
2318         auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2319         (dt += dur!"seconds"(92)) -= dur!"days"(-500);
2320         assert(dt == DateTime(2001, 6, 14, 9, 8, 38));
2321 
2322         auto duration = dur!"seconds"(12);
2323         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2324         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2325         static assert(!__traits(compiles, cdt += duration));
2326         static assert(!__traits(compiles, idt += duration));
2327         static assert(!__traits(compiles, cdt -= duration));
2328         static assert(!__traits(compiles, idt -= duration));
2329     }
2330 
2331 
2332     /++
2333         Gives the difference between two $(LREF DateTime)s.
2334 
2335         The legal types of arithmetic for $(LREF DateTime) using this operator are
2336 
2337         $(BOOKTABLE,
2338         $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
2339         )
2340       +/
2341     Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
2342         if (op == "-")
2343     {
2344         immutable dateResult = _date - rhs.date;
2345         immutable todResult = _tod - rhs._tod;
2346 
2347         import core.time : dur;
2348         return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
2349     }
2350 
2351     @safe unittest
2352     {
2353         auto dt = DateTime(1999, 7, 6, 12, 30, 33);
2354 
2355         import core.time : dur;
2356         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
2357                dur!"seconds"(31_536_000));
2358         assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2359                dur!"seconds"(-31_536_000));
2360 
2361         assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2362                dur!"seconds"(26_78_400));
2363         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) ==
2364                dur!"seconds"(-26_78_400));
2365 
2366         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) ==
2367                dur!"seconds"(86_400));
2368         assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2369                dur!"seconds"(-86_400));
2370 
2371         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) ==
2372                dur!"seconds"(3600));
2373         assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2374                dur!"seconds"(-3600));
2375 
2376         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2377                dur!"seconds"(60));
2378         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) ==
2379                dur!"seconds"(-60));
2380 
2381         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2382                dur!"seconds"(1));
2383         assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) ==
2384                dur!"seconds"(-1));
2385 
2386         assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033));
2387         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033));
2388         assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367));
2389         assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367));
2390 
2391         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2392         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2393         assert(dt - dt == Duration.zero);
2394         assert(cdt - dt == Duration.zero);
2395         assert(idt - dt == Duration.zero);
2396 
2397         assert(dt - cdt == Duration.zero);
2398         assert(cdt - cdt == Duration.zero);
2399         assert(idt - cdt == Duration.zero);
2400 
2401         assert(dt - idt == Duration.zero);
2402         assert(cdt - idt == Duration.zero);
2403         assert(idt - idt == Duration.zero);
2404     }
2405 
2406 
2407     /++
2408         Returns the difference between the two $(LREF DateTime)s in months.
2409 
2410         To get the difference in years, subtract the year property
2411         of two $(LREF DateTime)s. To get the difference in days or weeks,
2412         subtract the $(LREF DateTime)s themselves and use the
2413         $(REF Duration, core,time) that results. Because converting between
2414         months and smaller units requires a specific date (which
2415         $(REF Duration, core,time)s don't have), getting the difference in
2416         months requires some math using both the year and month properties, so
2417         this is a convenience function for getting the difference in months.
2418 
2419         Note that the number of days in the months or how far into the month
2420         either date is is irrelevant. It is the difference in the month property
2421         combined with the difference in years * 12. So, for instance,
2422         December 31st and January 1st are one month apart just as December 1st
2423         and January 31st are one month apart.
2424 
2425         Params:
2426             rhs = The $(LREF DateTime) to subtract from this one.
2427       +/
2428     int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
2429     {
2430         return _date.diffMonths(rhs._date);
2431     }
2432 
2433     ///
2434     @safe unittest
2435     {
2436         assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
2437                    DateTime(1999, 1, 31, 23, 59, 59)) == 1);
2438 
2439         assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
2440                    DateTime(1999, 2, 1, 12, 3, 42)) == -1);
2441 
2442         assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
2443                    DateTime(1999, 1, 1, 2, 4, 7)) == 2);
2444 
2445         assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
2446                    DateTime(1999, 3, 31, 0, 30, 58)) == -2);
2447     }
2448 
2449     @safe unittest
2450     {
2451         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2452         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2453         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2454         assert(dt.diffMonths(dt) == 0);
2455         assert(cdt.diffMonths(dt) == 0);
2456         assert(idt.diffMonths(dt) == 0);
2457 
2458         assert(dt.diffMonths(cdt) == 0);
2459         assert(cdt.diffMonths(cdt) == 0);
2460         assert(idt.diffMonths(cdt) == 0);
2461 
2462         assert(dt.diffMonths(idt) == 0);
2463         assert(cdt.diffMonths(idt) == 0);
2464         assert(idt.diffMonths(idt) == 0);
2465     }
2466 
2467 
2468     /++
2469         Whether this $(LREF DateTime) is in a leap year.
2470      +/
2471     @property bool isLeapYear() const @safe pure nothrow @nogc
2472     {
2473         return _date.isLeapYear;
2474     }
2475 
2476     @safe unittest
2477     {
2478         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2479         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2480         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2481         assert(!dt.isLeapYear);
2482         assert(!cdt.isLeapYear);
2483         assert(!idt.isLeapYear);
2484     }
2485 
2486 
2487     /++
2488         Day of the week this $(LREF DateTime) is on.
2489       +/
2490     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
2491     {
2492         return _date.dayOfWeek;
2493     }
2494 
2495     @safe unittest
2496     {
2497         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2498         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2499         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2500         assert(dt.dayOfWeek == DayOfWeek.tue);
2501         assert(cdt.dayOfWeek == DayOfWeek.tue);
2502         assert(idt.dayOfWeek == DayOfWeek.tue);
2503     }
2504 
2505 
2506     /++
2507         Day of the year this $(LREF DateTime) is on.
2508       +/
2509     @property ushort dayOfYear() const @safe pure nothrow @nogc
2510     {
2511         return _date.dayOfYear;
2512     }
2513 
2514     ///
2515     @safe unittest
2516     {
2517         assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
2518         assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
2519         assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
2520     }
2521 
2522     @safe unittest
2523     {
2524         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2525         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2526         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2527         assert(dt.dayOfYear == 187);
2528         assert(cdt.dayOfYear == 187);
2529         assert(idt.dayOfYear == 187);
2530     }
2531 
2532 
2533     /++
2534         Day of the year.
2535 
2536         Params:
2537             day = The day of the year to set which day of the year this
2538                   $(LREF DateTime) is on.
2539       +/
2540     @property void dayOfYear(int day) @safe pure
2541     {
2542         _date.dayOfYear = day;
2543     }
2544 
2545     @safe unittest
2546     {
2547         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2548         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2549         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2550         dt.dayOfYear = 12;
2551         assert(dt.dayOfYear == 12);
2552         static assert(!__traits(compiles, cdt.dayOfYear = 12));
2553         static assert(!__traits(compiles, idt.dayOfYear = 12));
2554     }
2555 
2556 
2557     /++
2558         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2559      +/
2560     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
2561     {
2562         return _date.dayOfGregorianCal;
2563     }
2564 
2565     ///
2566     @safe unittest
2567     {
2568         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
2569         assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
2570         assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
2571 
2572         assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
2573         assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
2574         assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
2575 
2576         assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
2577         assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
2578     }
2579 
2580     @safe unittest
2581     {
2582         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2583         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2584         assert(cdt.dayOfGregorianCal == 729_941);
2585         assert(idt.dayOfGregorianCal == 729_941);
2586     }
2587 
2588 
2589     /++
2590         The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2591         Setting this property does not affect the time portion of
2592         $(LREF DateTime).
2593 
2594         Params:
2595             days = The day of the Gregorian Calendar to set this $(LREF DateTime)
2596                    to.
2597      +/
2598     @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
2599     {
2600         _date.dayOfGregorianCal = days;
2601     }
2602 
2603     ///
2604     @safe unittest
2605     {
2606         auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
2607         dt.dayOfGregorianCal = 1;
2608         assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
2609 
2610         dt.dayOfGregorianCal = 365;
2611         assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
2612 
2613         dt.dayOfGregorianCal = 366;
2614         assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
2615 
2616         dt.dayOfGregorianCal = 0;
2617         assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
2618 
2619         dt.dayOfGregorianCal = -365;
2620         assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
2621 
2622         dt.dayOfGregorianCal = -366;
2623         assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
2624 
2625         dt.dayOfGregorianCal = 730_120;
2626         assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
2627 
2628         dt.dayOfGregorianCal = 734_137;
2629         assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
2630     }
2631 
2632     @safe unittest
2633     {
2634         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2635         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2636         static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7));
2637         static assert(!__traits(compiles, idt.dayOfGregorianCal = 7));
2638     }
2639 
2640 
2641     /++
2642         The ISO 8601 week of the year that this $(LREF DateTime) is in.
2643 
2644         See_Also:
2645             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2646       +/
2647     @property ubyte isoWeek() const @safe pure nothrow
2648     {
2649         return _date.isoWeek;
2650     }
2651 
2652     @safe unittest
2653     {
2654         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2655         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2656         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2657         assert(dt.isoWeek == 27);
2658         assert(cdt.isoWeek == 27);
2659         assert(idt.isoWeek == 27);
2660     }
2661 
2662 
2663     /++
2664         The year of the ISO 8601 week calendar that this $(LREF DateTime) is in.
2665 
2666         See_Also:
2667             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2668       +/
2669     @property short isoWeekYear() const @safe pure nothrow
2670     {
2671         return _date.isoWeekYear;
2672     }
2673 
2674 
2675     /++
2676         $(LREF DateTime) for the last day in the month that this
2677         $(LREF DateTime) is in. The time portion of endOfMonth is always
2678         23:59:59.
2679       +/
2680     @property DateTime endOfMonth() const @safe pure nothrow
2681     {
2682         try
2683             return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
2684         catch (Exception e)
2685             assert(0, "DateTime constructor threw.");
2686     }
2687 
2688     ///
2689     @safe unittest
2690     {
2691         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
2692                DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
2693 
2694         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
2695                DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
2696 
2697         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
2698                DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
2699 
2700         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
2701                DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
2702     }
2703 
2704     @safe unittest
2705     {
2706         // Test A.D.
2707         assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59));
2708         assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59));
2709         assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59));
2710         assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59));
2711         assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59));
2712         assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59));
2713         assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59));
2714         assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2715         assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59));
2716         assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59));
2717         assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59));
2718         assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59));
2719         assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59));
2720 
2721         // Test B.C.
2722         assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59));
2723         assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59));
2724         assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59));
2725         assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59));
2726         assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59));
2727         assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59));
2728         assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59));
2729         assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59));
2730         assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59));
2731         assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59));
2732         assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59));
2733         assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59));
2734         assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59));
2735 
2736         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2737         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2738         assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2739         assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2740     }
2741 
2742 
2743     /++
2744         The last day in the month that this $(LREF DateTime) is in.
2745       +/
2746     @property ubyte daysInMonth() const @safe pure nothrow @nogc
2747     {
2748         return _date.daysInMonth;
2749     }
2750 
2751     ///
2752     @safe unittest
2753     {
2754         assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
2755         assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
2756         assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
2757         assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
2758     }
2759 
2760     @safe unittest
2761     {
2762         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2763         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2764         assert(cdt.daysInMonth == 31);
2765         assert(idt.daysInMonth == 31);
2766     }
2767 
2768 
2769     /++
2770         Whether the current year is a date in A.D.
2771       +/
2772     @property bool isAD() const @safe pure nothrow @nogc
2773     {
2774         return _date.isAD;
2775     }
2776 
2777     ///
2778     @safe unittest
2779     {
2780         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
2781         assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
2782         assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
2783         assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
2784     }
2785 
2786     @safe unittest
2787     {
2788         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2789         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2790         assert(cdt.isAD);
2791         assert(idt.isAD);
2792     }
2793 
2794 
2795     /++
2796         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
2797         $(LREF DateTime) at the given time. For example, prior to noon,
2798         1996-03-31 would be the Julian day number 2_450_173, so this function
2799         returns 2_450_173, while from noon onward, the julian day number would
2800         be 2_450_174, so this function returns 2_450_174.
2801       +/
2802     @property long julianDay() const @safe pure nothrow @nogc
2803     {
2804         if (_tod._hour < 12)
2805             return _date.julianDay - 1;
2806         else
2807             return _date.julianDay;
2808     }
2809 
2810     @safe unittest
2811     {
2812         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1);
2813         assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0);
2814 
2815         assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424);
2816         assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425);
2817 
2818         assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425);
2819         assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426);
2820 
2821         assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160);
2822         assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161);
2823 
2824         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000);
2825         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001);
2826 
2827         assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973);
2828         assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974);
2829 
2830         assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173);
2831         assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174);
2832 
2833         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432);
2834         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433);
2835 
2836         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2837         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2838         assert(cdt.julianDay == 2_451_366);
2839         assert(idt.julianDay == 2_451_366);
2840     }
2841 
2842 
2843     /++
2844         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
2845         time on this date (since, the modified Julian day changes at midnight).
2846       +/
2847     @property long modJulianDay() const @safe pure nothrow @nogc
2848     {
2849         return _date.modJulianDay;
2850     }
2851 
2852     @safe unittest
2853     {
2854         assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0);
2855         assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0);
2856 
2857         assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432);
2858         assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432);
2859 
2860         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2861         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2862         assert(cdt.modJulianDay == 51_365);
2863         assert(idt.modJulianDay == 51_365);
2864     }
2865 
2866 
2867     /++
2868         Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`.
2869         If `writer` is set, the resulting string will be written directly to it.
2870 
2871         Params:
2872             writer = A `char` accepting
2873             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2874         Returns:
2875             A `string` when not using an output range; `void` otherwise.
2876       +/
2877     string toISOString() const @safe pure nothrow
2878     {
2879         import std.array : appender;
2880         auto w = appender!string();
2881         w.reserve(18);
2882         try
2883             toISOString(w);
2884         catch (Exception e)
2885             assert(0, "toISOString() threw.");
2886         return w.data;
2887     }
2888 
2889     /// ditto
2890     void toISOString(W)(ref W writer) const
2891     if (isOutputRange!(W, char))
2892     {
2893         import std.format.write : formattedWrite;
2894         _date.toISOString(writer);
2895         formattedWrite!("T%02d%02d%02d")(
2896             writer,
2897             _tod._hour,
2898             _tod._minute,
2899             _tod._second
2900         );
2901     }
2902 
2903     ///
2904     @safe unittest
2905     {
2906         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
2907                "20100704T070612");
2908 
2909         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
2910                "19981225T021500");
2911 
2912         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
2913                "00000105T230959");
2914 
2915         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
2916                "-00040105T000002");
2917     }
2918 
2919     @safe unittest
2920     {
2921         // Test A.D.
2922         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000");
2923         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612");
2924         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459");
2925         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959");
2926         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101");
2927 
2928         // Test B.C.
2929         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204");
2930         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000");
2931         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612");
2932         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459");
2933         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959");
2934         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101");
2935 
2936         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2937         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2938         assert(cdt.toISOString() == "19990706T123033");
2939         assert(idt.toISOString() == "19990706T123033");
2940     }
2941 
2942 
2943     /++
2944         Converts this $(LREF DateTime) to a string with the format
2945         `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting
2946         string will be written directly to it.
2947 
2948         Params:
2949             writer = A `char` accepting
2950             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2951         Returns:
2952             A `string` when not using an output range; `void` otherwise.
2953       +/
2954     string toISOExtString() const @safe pure nothrow
2955     {
2956         import std.array : appender;
2957         auto w = appender!string();
2958         w.reserve(20);
2959         try
2960             toISOExtString(w);
2961         catch (Exception e)
2962             assert(0, "toISOExtString() threw.");
2963         return w.data;
2964     }
2965 
2966     /// ditto
2967     void toISOExtString(W)(ref W writer) const
2968     if (isOutputRange!(W, char))
2969     {
2970         import std.format.write : formattedWrite;
2971         _date.toISOExtString(writer);
2972         formattedWrite!("T%02d:%02d:%02d")(
2973             writer,
2974             _tod._hour,
2975             _tod._minute,
2976             _tod._second
2977         );
2978     }
2979 
2980     ///
2981     @safe unittest
2982     {
2983         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
2984                "2010-07-04T07:06:12");
2985 
2986         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
2987                "1998-12-25T02:15:00");
2988 
2989         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
2990                "0000-01-05T23:09:59");
2991 
2992         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
2993                "-0004-01-05T00:00:02");
2994     }
2995 
2996     @safe unittest
2997     {
2998         // Test A.D.
2999         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00");
3000         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12");
3001         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59");
3002         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59");
3003         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01");
3004 
3005         // Test B.C.
3006         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04");
3007         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00");
3008         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12");
3009         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59");
3010         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59");
3011         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01");
3012 
3013         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3014         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3015         assert(cdt.toISOExtString() == "1999-07-06T12:30:33");
3016         assert(idt.toISOExtString() == "1999-07-06T12:30:33");
3017     }
3018 
3019     /++
3020         Converts this $(LREF DateTime) to a string with the format
3021         `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting
3022         string will be written directly to it.
3023 
3024         Params:
3025             writer = A `char` accepting
3026             $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3027         Returns:
3028             A `string` when not using an output range; `void` otherwise.
3029       +/
3030     string toSimpleString() const @safe pure nothrow
3031     {
3032         import std.array : appender;
3033         auto w = appender!string();
3034         w.reserve(22);
3035         try
3036             toSimpleString(w);
3037         catch (Exception e)
3038             assert(0, "toSimpleString() threw.");
3039         return w.data;
3040     }
3041 
3042     /// ditto
3043     void toSimpleString(W)(ref W writer) const
3044     if (isOutputRange!(W, char))
3045     {
3046         import std.format.write : formattedWrite;
3047         _date.toSimpleString(writer);
3048         formattedWrite!(" %02d:%02d:%02d")(
3049             writer,
3050             _tod._hour,
3051             _tod._minute,
3052             _tod._second
3053         );
3054     }
3055 
3056     ///
3057     @safe unittest
3058     {
3059         assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
3060                "2010-Jul-04 07:06:12");
3061 
3062         assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
3063                "1998-Dec-25 02:15:00");
3064 
3065         assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
3066                "0000-Jan-05 23:09:59");
3067 
3068         assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
3069                "-0004-Jan-05 00:00:02");
3070     }
3071 
3072     @safe unittest
3073     {
3074         // Test A.D.
3075         assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00");
3076         assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12");
3077         assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59");
3078         assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59");
3079         assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01");
3080 
3081         // Test B.C.
3082         assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04");
3083         assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00");
3084         assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12");
3085         assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59");
3086         assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59");
3087         assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01");
3088 
3089         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3090         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3091         assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33");
3092         assert(idt.toSimpleString() == "1999-Jul-06 12:30:33");
3093     }
3094 
3095 
3096     /++
3097         Converts this $(LREF DateTime) to a string.
3098 
3099         This function exists to make it easy to convert a $(LREF DateTime) to a
3100         string for code that does not care what the exact format is - just that
3101         it presents the information in a clear manner. It also makes it easy to
3102         simply convert a $(LREF DateTime) to a string when using functions such
3103         as `to!string`, `format`, or `writeln` which use toString to convert
3104         user-defined types. So, it is unlikely that much code will call
3105         toString directly.
3106 
3107         The format of the string is purposefully unspecified, and code that
3108         cares about the format of the string should use `toISOString`,
3109         `toISOExtString`, `toSimpleString`, or some other custom formatting
3110         function that explicitly generates the format that the code needs. The
3111         reason is that the code is then clear about what format it's using,
3112         making it less error-prone to maintain the code and interact with other
3113         software that consumes the generated strings. It's for this same reason
3114         that $(LREF DateTime) has no `fromString` function, whereas it does have
3115         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
3116 
3117         The format returned by toString may or may not change in the future.
3118       +/
3119     string toString() const @safe pure nothrow
3120     {
3121         return toSimpleString();
3122     }
3123 
3124     @safe unittest
3125     {
3126         auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3127         const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3128         immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3129         assert(dt.toString());
3130         assert(cdt.toString());
3131         assert(idt.toString());
3132     }
3133 
3134     /// ditto
3135     void toString(W)(ref W writer) const
3136     if (isOutputRange!(W, char))
3137     {
3138         toSimpleString(writer);
3139     }
3140 
3141     /++
3142         Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
3143         Whitespace is stripped from the given string.
3144 
3145         Params:
3146             isoString = A string formatted in the ISO format for dates and times.
3147 
3148         Throws:
3149             $(REF DateTimeException,std,datetime,date) if the given string is
3150             not in the ISO format or if the resulting $(LREF DateTime) would not
3151             be valid.
3152       +/
3153     static DateTime fromISOString(S)(scope const S isoString) @safe pure
3154         if (isSomeString!S)
3155     {
3156         import std.algorithm.searching : countUntil;
3157         import std.exception : enforce;
3158         import std.format : format;
3159         import std.string : strip;
3160         import std.utf : byCodeUnit;
3161 
3162         auto str = strip(isoString);
3163 
3164         enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
3165         auto t = str.byCodeUnit.countUntil('T');
3166 
3167         enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
3168 
3169         immutable date = Date.fromISOString(str[0 .. t]);
3170         immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
3171 
3172         return DateTime(date, tod);
3173     }
3174 
3175     ///
3176     @safe unittest
3177     {
3178         assert(DateTime.fromISOString("20100704T070612") ==
3179                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3180 
3181         assert(DateTime.fromISOString("19981225T021500") ==
3182                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3183 
3184         assert(DateTime.fromISOString("00000105T230959") ==
3185                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3186 
3187         assert(DateTime.fromISOString("-00040105T000002") ==
3188                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3189 
3190         assert(DateTime.fromISOString(" 20100704T070612 ") ==
3191                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3192     }
3193 
3194     @safe unittest
3195     {
3196         assertThrown!DateTimeException(DateTime.fromISOString(""));
3197         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3198         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3199         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3200         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3201         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3202 
3203         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3204         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3205         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3206         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3207         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3208 
3209         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3210         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3211         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3212         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3213         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3214         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3215 
3216         assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
3217         assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
3218 
3219         assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3220         assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3221         assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3222         assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3223         assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3224         assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3225         assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3226     }
3227 
3228     // https://issues.dlang.org/show_bug.cgi?id=17801
3229     @safe unittest
3230     {
3231         import std.conv : to;
3232         import std.meta : AliasSeq;
3233         static foreach (C; AliasSeq!(char, wchar, dchar))
3234         {
3235             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3236                 assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
3237         }
3238     }
3239 
3240 
3241     /++
3242         Creates a $(LREF DateTime) from a string with the format
3243         YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string.
3244 
3245         Params:
3246             isoExtString = A string formatted in the ISO Extended format for dates
3247                            and times.
3248 
3249         Throws:
3250             $(REF DateTimeException,std,datetime,date) if the given string is
3251             not in the ISO Extended format or if the resulting $(LREF DateTime)
3252             would not be valid.
3253       +/
3254     static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
3255         if (isSomeString!(S))
3256     {
3257         import std.algorithm.searching : countUntil;
3258         import std.exception : enforce;
3259         import std.format : format;
3260         import std.string : strip;
3261         import std.utf : byCodeUnit;
3262 
3263         auto str = strip(isoExtString);
3264 
3265         enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
3266         auto t = str.byCodeUnit.countUntil('T');
3267 
3268         enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
3269 
3270         immutable date = Date.fromISOExtString(str[0 .. t]);
3271         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3272 
3273         return DateTime(date, tod);
3274     }
3275 
3276     ///
3277     @safe unittest
3278     {
3279         assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
3280                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3281 
3282         assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
3283                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3284 
3285         assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
3286                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3287 
3288         assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
3289                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3290 
3291         assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
3292                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3293     }
3294 
3295     @safe unittest
3296     {
3297         assertThrown!DateTimeException(DateTime.fromISOExtString(""));
3298         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
3299         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
3300         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
3301         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
3302         assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
3303 
3304         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
3305         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3306         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3307         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
3308         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
3309         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
3310 
3311         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
3312         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
3313         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
3314         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
3315 
3316         assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
3317         assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
3318 
3319         assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3320         assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3321         assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3322         assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3323         assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3324         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3325         assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3326     }
3327 
3328     // https://issues.dlang.org/show_bug.cgi?id=17801
3329     @safe unittest
3330     {
3331         import std.conv : to;
3332         import std.meta : AliasSeq;
3333         static foreach (C; AliasSeq!(char, wchar, dchar))
3334         {
3335             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3336                 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3337         }
3338     }
3339 
3340 
3341     /++
3342         Creates a $(LREF DateTime) from a string with the format
3343         YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
3344 
3345         Params:
3346             simpleString = A string formatted in the way that toSimpleString
3347                            formats dates and times.
3348 
3349         Throws:
3350             $(REF DateTimeException,std,datetime,date) if the given string is
3351             not in the correct format or if the resulting $(LREF DateTime)
3352             would not be valid.
3353       +/
3354     static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
3355         if (isSomeString!(S))
3356     {
3357         import std.algorithm.searching : countUntil;
3358         import std.exception : enforce;
3359         import std.format : format;
3360         import std.string : strip;
3361         import std.utf : byCodeUnit;
3362 
3363         auto str = strip(simpleString);
3364 
3365         enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
3366         auto t = str.byCodeUnit.countUntil(' ');
3367 
3368         enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
3369 
3370         immutable date = Date.fromSimpleString(str[0 .. t]);
3371         immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3372 
3373         return DateTime(date, tod);
3374     }
3375 
3376     ///
3377     @safe unittest
3378     {
3379         assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
3380                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3381         assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
3382                DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3383         assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
3384                DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3385         assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
3386                DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3387         assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
3388                DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3389     }
3390 
3391     @safe unittest
3392     {
3393         assertThrown!DateTimeException(DateTime.fromISOString(""));
3394         assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3395         assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3396         assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3397         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3398         assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3399 
3400         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3401         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3402         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3403         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3404         assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3405 
3406         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3407         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3408         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3409         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3410         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3411         assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3412 
3413         assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
3414         assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
3415 
3416         assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
3417                DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3418         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
3419                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3420         assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
3421                DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3422         assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
3423                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3424         assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
3425                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3426         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
3427                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3428         assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
3429                DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3430     }
3431 
3432     // https://issues.dlang.org/show_bug.cgi?id=17801
3433     @safe unittest
3434     {
3435         import std.conv : to;
3436         import std.meta : AliasSeq;
3437         static foreach (C; AliasSeq!(char, wchar, dchar))
3438         {
3439             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3440                 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3441         }
3442     }
3443 
3444 
3445     /++
3446         Returns the $(LREF DateTime) farthest in the past which is representable
3447         by $(LREF DateTime).
3448       +/
3449     @property static DateTime min() @safe pure nothrow @nogc
3450     out(result)
3451     {
3452         assert(result._date == Date.min);
3453         assert(result._tod == TimeOfDay.min);
3454     }
3455     do
3456     {
3457         auto dt = DateTime.init;
3458         dt._date._year = short.min;
3459         dt._date._month = Month.jan;
3460         dt._date._day = 1;
3461 
3462         return dt;
3463     }
3464 
3465     @safe unittest
3466     {
3467         assert(DateTime.min.year < 0);
3468         assert(DateTime.min < DateTime.max);
3469     }
3470 
3471 
3472     /++
3473         Returns the $(LREF DateTime) farthest in the future which is
3474         representable by $(LREF DateTime).
3475       +/
3476     @property static DateTime max() @safe pure nothrow @nogc
3477     out(result)
3478     {
3479         assert(result._date == Date.max);
3480         assert(result._tod == TimeOfDay.max);
3481     }
3482     do
3483     {
3484         auto dt = DateTime.init;
3485         dt._date._year = short.max;
3486         dt._date._month = Month.dec;
3487         dt._date._day = 31;
3488         dt._tod._hour = TimeOfDay.maxHour;
3489         dt._tod._minute = TimeOfDay.maxMinute;
3490         dt._tod._second = TimeOfDay.maxSecond;
3491 
3492         return dt;
3493     }
3494 
3495     @safe unittest
3496     {
3497         assert(DateTime.max.year > 0);
3498         assert(DateTime.max > DateTime.min);
3499     }
3500 
3501 
3502 private:
3503 
3504     /+
3505         Add seconds to the time of day. Negative values will subtract. If the
3506         number of seconds overflows (or underflows), then the seconds will wrap,
3507         increasing (or decreasing) the number of minutes accordingly. The
3508         same goes for any larger units.
3509 
3510         Params:
3511             seconds = The number of seconds to add to this $(LREF DateTime).
3512       +/
3513     ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
3514     {
3515         import core.time : convert;
3516         long hnsecs = convert!("seconds", "hnsecs")(seconds);
3517         hnsecs += convert!("hours", "hnsecs")(_tod._hour);
3518         hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
3519         hnsecs += convert!("seconds", "hnsecs")(_tod._second);
3520 
3521         auto days = splitUnitsFromHNSecs!"days"(hnsecs);
3522 
3523         if (hnsecs < 0)
3524         {
3525             hnsecs += convert!("days", "hnsecs")(1);
3526             --days;
3527         }
3528 
3529         _date._addDays(days);
3530 
3531         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
3532         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
3533         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
3534 
3535         _tod._hour = cast(ubyte) newHours;
3536         _tod._minute = cast(ubyte) newMinutes;
3537         _tod._second = cast(ubyte) newSeconds;
3538 
3539         return this;
3540     }
3541 
3542     @safe unittest
3543     {
3544         static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
3545         {
3546             orig._addSeconds(seconds);
3547             assert(orig == expected);
3548         }
3549 
3550         // Test A.D.
3551         testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
3552         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
3553         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
3554         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
3555         testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
3556         testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
3557         testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
3558         testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
3559         testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
3560         testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
3561         testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
3562         testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
3563         testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
3564         testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
3565 
3566         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
3567         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
3568         testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
3569         testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
3570         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
3571         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
3572         testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
3573         testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
3574         testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
3575 
3576         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
3577         testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
3578         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
3579         testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
3580         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
3581         testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
3582         testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
3583         testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
3584         testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
3585         testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
3586         testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
3587         testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
3588         testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
3589 
3590         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
3591         testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
3592         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
3593         testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
3594         testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
3595         testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
3596 
3597         testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
3598         testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
3599         testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
3600 
3601         testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
3602         testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
3603         testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
3604 
3605         testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
3606         testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
3607         testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
3608 
3609         testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
3610         testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
3611         testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
3612 
3613         testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
3614         testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
3615         testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
3616 
3617         testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
3618         testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
3619         testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
3620 
3621         // Test B.C.
3622         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
3623         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
3624         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
3625         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
3626         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
3627         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
3628         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
3629         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
3630         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
3631         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
3632         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
3633         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
3634         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
3635         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
3636 
3637         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
3638         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
3639         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
3640         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
3641         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
3642         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
3643         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
3644         testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
3645         testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
3646 
3647         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
3648         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
3649         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
3650         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
3651         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
3652         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
3653         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
3654         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
3655         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
3656         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
3657         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
3658         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
3659         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
3660 
3661         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
3662         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
3663         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
3664         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
3665         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
3666         testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
3667         testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
3668 
3669         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
3670         testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
3671         testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
3672 
3673         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
3674         testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
3675         testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
3676 
3677         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
3678         testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
3679         testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
3680 
3681         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
3682         testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
3683         testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
3684 
3685         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
3686         testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
3687         testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
3688 
3689         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
3690         testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
3691         testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
3692 
3693         // Test Both
3694         testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
3695         testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
3696 
3697         testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
3698         testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
3699 
3700         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
3701         testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
3702 
3703         testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
3704         testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
3705 
3706         const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3707         immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3708         static assert(!__traits(compiles, cdt._addSeconds(4)));
3709         static assert(!__traits(compiles, idt._addSeconds(4)));
3710     }
3711 
3712 
3713     Date      _date;
3714     TimeOfDay _tod;
3715 }
3716 
3717 ///
3718 @safe pure unittest
3719 {
3720     import core.time : days, seconds;
3721 
3722     auto dt = DateTime(2000, 6, 1, 10, 30, 0);
3723 
3724     assert(dt.date == Date(2000, 6, 1));
3725     assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
3726     assert(dt.dayOfYear == 153);
3727     assert(dt.dayOfWeek == DayOfWeek.thu);
3728 
3729     dt += 10.days + 100.seconds;
3730     assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
3731 
3732     assert(dt.toISOExtString() == "2000-06-11T10:31:40");
3733     assert(dt.toISOString() == "20000611T103140");
3734     assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
3735 
3736     assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3737     assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
3738     assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3739 }
3740 
3741 /++
3742     Represents a date in the
3743     $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
3744     Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
3745     are A.D. Non-positive years are B.C.
3746 
3747     Year, month, and day are kept separately internally so that `Date` is
3748     optimized for calendar-based operations.
3749 
3750     `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
3751     leap year calculations for its entire length. As per
3752     $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
3753     year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
3754     as a positive integer with 1 B.C. being the year prior to 1 A.D.
3755 
3756     Year 0 is a leap year.
3757  +/
3758 struct Date
3759 {
3760 public:
3761 
3762     /++
3763         Throws:
3764             $(REF DateTimeException,std,datetime,date) if the resulting
3765             $(LREF Date) would not be valid.
3766 
3767         Params:
3768             year  = Year of the Gregorian Calendar. Positive values are A.D.
3769                     Non-positive values are B.C. with year 0 being the year
3770                     prior to 1 A.D.
3771             month = Month of the year (January is 1).
3772             day   = Day of the month.
3773      +/
3774     this(int year, int month, int day) @safe pure
3775     {
3776         enforceValid!"months"(cast(Month) month);
3777         enforceValid!"days"(year, cast(Month) month, day);
3778 
3779         _year  = cast(short) year;
3780         _month = cast(Month) month;
3781         _day   = cast(ubyte) day;
3782     }
3783 
3784     @safe unittest
3785     {
3786         import std.exception : assertNotThrown;
3787         assert(Date(1, 1, 1) == Date.init);
3788 
3789         static void testDate(Date date, int year, int month, int day)
3790         {
3791             assert(date._year == year);
3792             assert(date._month == month);
3793             assert(date._day == day);
3794         }
3795 
3796         testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
3797         testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
3798         testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
3799 
3800         // Test A.D.
3801         assertThrown!DateTimeException(Date(1, 0, 1));
3802         assertThrown!DateTimeException(Date(1, 1, 0));
3803         assertThrown!DateTimeException(Date(1999, 13, 1));
3804         assertThrown!DateTimeException(Date(1999, 1, 32));
3805         assertThrown!DateTimeException(Date(1999, 2, 29));
3806         assertThrown!DateTimeException(Date(2000, 2, 30));
3807         assertThrown!DateTimeException(Date(1999, 3, 32));
3808         assertThrown!DateTimeException(Date(1999, 4, 31));
3809         assertThrown!DateTimeException(Date(1999, 5, 32));
3810         assertThrown!DateTimeException(Date(1999, 6, 31));
3811         assertThrown!DateTimeException(Date(1999, 7, 32));
3812         assertThrown!DateTimeException(Date(1999, 8, 32));
3813         assertThrown!DateTimeException(Date(1999, 9, 31));
3814         assertThrown!DateTimeException(Date(1999, 10, 32));
3815         assertThrown!DateTimeException(Date(1999, 11, 31));
3816         assertThrown!DateTimeException(Date(1999, 12, 32));
3817 
3818         assertNotThrown!DateTimeException(Date(1999, 1, 31));
3819         assertNotThrown!DateTimeException(Date(1999, 2, 28));
3820         assertNotThrown!DateTimeException(Date(2000, 2, 29));
3821         assertNotThrown!DateTimeException(Date(1999, 3, 31));
3822         assertNotThrown!DateTimeException(Date(1999, 4, 30));
3823         assertNotThrown!DateTimeException(Date(1999, 5, 31));
3824         assertNotThrown!DateTimeException(Date(1999, 6, 30));
3825         assertNotThrown!DateTimeException(Date(1999, 7, 31));
3826         assertNotThrown!DateTimeException(Date(1999, 8, 31));
3827         assertNotThrown!DateTimeException(Date(1999, 9, 30));
3828         assertNotThrown!DateTimeException(Date(1999, 10, 31));
3829         assertNotThrown!DateTimeException(Date(1999, 11, 30));
3830         assertNotThrown!DateTimeException(Date(1999, 12, 31));
3831 
3832         // Test B.C.
3833         assertNotThrown!DateTimeException(Date(0, 1, 1));
3834         assertNotThrown!DateTimeException(Date(-1, 1, 1));
3835         assertNotThrown!DateTimeException(Date(-1, 12, 31));
3836         assertNotThrown!DateTimeException(Date(-1, 2, 28));
3837         assertNotThrown!DateTimeException(Date(-4, 2, 29));
3838 
3839         assertThrown!DateTimeException(Date(-1, 2, 29));
3840         assertThrown!DateTimeException(Date(-2, 2, 29));
3841         assertThrown!DateTimeException(Date(-3, 2, 29));
3842     }
3843 
3844 
3845     /++
3846         Params:
3847             day = The Xth day of the Gregorian Calendar that the constructed
3848                   $(LREF Date) will be for.
3849      +/
3850     this(int day) @safe pure nothrow @nogc
3851     {
3852         if (day > 0)
3853         {
3854             int years = (day / daysIn400Years) * 400 + 1;
3855             day %= daysIn400Years;
3856 
3857             {
3858                 immutable tempYears = day / daysIn100Years;
3859 
3860                 if (tempYears == 4)
3861                 {
3862                     years += 300;
3863                     day -= daysIn100Years * 3;
3864                 }
3865                 else
3866                 {
3867                     years += tempYears * 100;
3868                     day %= daysIn100Years;
3869                 }
3870             }
3871 
3872             years += (day / daysIn4Years) * 4;
3873             day %= daysIn4Years;
3874 
3875             {
3876                 immutable tempYears = day / daysInYear;
3877 
3878                 if (tempYears == 4)
3879                 {
3880                     years += 3;
3881                     day -= daysInYear * 3;
3882                 }
3883                 else
3884                 {
3885                     years += tempYears;
3886                     day %= daysInYear;
3887                 }
3888             }
3889 
3890             if (day == 0)
3891             {
3892                 _year = cast(short)(years - 1);
3893                 _month = Month.dec;
3894                 _day = 31;
3895             }
3896             else
3897             {
3898                 _year = cast(short) years;
3899 
3900                 setDayOfYear(day);
3901             }
3902         }
3903         else if (day <= 0 && -day < daysInLeapYear)
3904         {
3905             _year = 0;
3906 
3907             setDayOfYear(daysInLeapYear + day);
3908         }
3909         else
3910         {
3911             day += daysInLeapYear - 1;
3912             int years = (day / daysIn400Years) * 400 - 1;
3913             day %= daysIn400Years;
3914 
3915             {
3916                 immutable tempYears = day / daysIn100Years;
3917 
3918                 if (tempYears == -4)
3919                 {
3920                     years -= 300;
3921                     day += daysIn100Years * 3;
3922                 }
3923                 else
3924                 {
3925                     years += tempYears * 100;
3926                     day %= daysIn100Years;
3927                 }
3928             }
3929 
3930             years += (day / daysIn4Years) * 4;
3931             day %= daysIn4Years;
3932 
3933             {
3934                 immutable tempYears = day / daysInYear;
3935 
3936                 if (tempYears == -4)
3937                 {
3938                     years -= 3;
3939                     day += daysInYear * 3;
3940                 }
3941                 else
3942                 {
3943                     years += tempYears;
3944                     day %= daysInYear;
3945                 }
3946             }
3947 
3948             if (day == 0)
3949             {
3950                 _year = cast(short)(years + 1);
3951                 _month = Month.jan;
3952                 _day = 1;
3953             }
3954             else
3955             {
3956                 _year = cast(short) years;
3957                 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
3958 
3959                 setDayOfYear(newDoY);
3960             }
3961         }
3962     }
3963 
3964     @safe unittest
3965     {
3966         import std.range : chain;
3967 
3968         // Test A.D.
3969         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
3970             assert(Date(gd.day) == gd.date);
3971     }
3972 
3973 
3974     /++
3975         Compares this $(LREF Date) with the given $(LREF Date).
3976 
3977         Returns:
3978             $(BOOKTABLE,
3979             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
3980             $(TR $(TD this == rhs) $(TD 0))
3981             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
3982             )
3983      +/
3984     int opCmp(Date rhs) const @safe pure nothrow @nogc
3985     {
3986         if (_year < rhs._year)
3987             return -1;
3988         if (_year > rhs._year)
3989             return 1;
3990 
3991         if (_month < rhs._month)
3992             return -1;
3993         if (_month > rhs._month)
3994             return 1;
3995 
3996         if (_day < rhs._day)
3997             return -1;
3998         if (_day > rhs._day)
3999             return 1;
4000 
4001         return 0;
4002     }
4003 
4004     @safe unittest
4005     {
4006         // Test A.D.
4007         assert(Date(1, 1, 1).opCmp(Date.init) == 0);
4008 
4009         assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
4010         assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
4011         assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
4012 
4013         assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
4014         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
4015 
4016         assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
4017 
4018         assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
4019         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
4020         assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
4021         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
4022         assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
4023         assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
4024 
4025         assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
4026         assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4027         assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
4028         assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
4029         assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
4030         assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4031 
4032         // Test B.C.
4033         assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
4034         assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
4035         assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
4036         assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
4037 
4038         assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
4039         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
4040 
4041         assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
4042 
4043         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
4044         assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
4045         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
4046         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
4047         assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4048         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
4049 
4050         assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
4051         assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
4052         assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4053         assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
4054         assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
4055         assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
4056 
4057         // Test Both
4058         assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
4059         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
4060 
4061         assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
4062         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
4063 
4064         assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
4065         assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
4066 
4067         assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
4068         assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
4069 
4070         assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
4071         assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
4072 
4073         auto date = Date(1999, 7, 6);
4074         const cdate = Date(1999, 7, 6);
4075         immutable idate = Date(1999, 7, 6);
4076         assert(date.opCmp(date) == 0);
4077         assert(date.opCmp(cdate) == 0);
4078         assert(date.opCmp(idate) == 0);
4079         assert(cdate.opCmp(date) == 0);
4080         assert(cdate.opCmp(cdate) == 0);
4081         assert(cdate.opCmp(idate) == 0);
4082         assert(idate.opCmp(date) == 0);
4083         assert(idate.opCmp(cdate) == 0);
4084         assert(idate.opCmp(idate) == 0);
4085     }
4086 
4087 
4088     /++
4089         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4090         are B.C.
4091      +/
4092     @property short year() const @safe pure nothrow @nogc
4093     {
4094         return _year;
4095     }
4096 
4097     ///
4098     @safe unittest
4099     {
4100         assert(Date(1999, 7, 6).year == 1999);
4101         assert(Date(2010, 10, 4).year == 2010);
4102         assert(Date(-7, 4, 5).year == -7);
4103     }
4104 
4105     @safe unittest
4106     {
4107         assert(Date.init.year == 1);
4108         assert(Date(1999, 7, 6).year == 1999);
4109         assert(Date(-1999, 7, 6).year == -1999);
4110 
4111         const cdate = Date(1999, 7, 6);
4112         immutable idate = Date(1999, 7, 6);
4113         assert(cdate.year == 1999);
4114         assert(idate.year == 1999);
4115     }
4116 
4117     /++
4118         Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4119         are B.C.
4120 
4121         Params:
4122             year = The year to set this Date's year to.
4123 
4124         Throws:
4125             $(REF DateTimeException,std,datetime,date) if the new year is not
4126             a leap year and the resulting date would be on February 29th.
4127      +/
4128     @property void year(int year) @safe pure
4129     {
4130         enforceValid!"days"(year, _month, _day);
4131         _year = cast(short) year;
4132     }
4133 
4134     ///
4135     @safe unittest
4136     {
4137         assert(Date(1999, 7, 6).year == 1999);
4138         assert(Date(2010, 10, 4).year == 2010);
4139         assert(Date(-7, 4, 5).year == -7);
4140     }
4141 
4142     @safe unittest
4143     {
4144         static void testDateInvalid(Date date, int year)
4145         {
4146             date.year = year;
4147         }
4148 
4149         static void testDate(Date date, int year, Date expected)
4150         {
4151             date.year = year;
4152             assert(date == expected);
4153         }
4154 
4155         assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
4156 
4157         testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
4158         testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
4159         testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
4160 
4161         const cdate = Date(1999, 7, 6);
4162         immutable idate = Date(1999, 7, 6);
4163         static assert(!__traits(compiles, cdate.year = 1999));
4164         static assert(!__traits(compiles, idate.year = 1999));
4165     }
4166 
4167 
4168     /++
4169         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4170 
4171         Throws:
4172             $(REF DateTimeException,std,datetime,date) if `isAD` is true.
4173      +/
4174     @property ushort yearBC() const @safe pure
4175     {
4176         import std.format : format;
4177 
4178         if (isAD)
4179             throw new DateTimeException(format("Year %s is A.D.", _year));
4180         return cast(ushort)((_year * -1) + 1);
4181     }
4182 
4183     ///
4184     @safe unittest
4185     {
4186         assert(Date(0, 1, 1).yearBC == 1);
4187         assert(Date(-1, 1, 1).yearBC == 2);
4188         assert(Date(-100, 1, 1).yearBC == 101);
4189     }
4190 
4191     @safe unittest
4192     {
4193         assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
4194 
4195         auto date = Date(0, 7, 6);
4196         const cdate = Date(0, 7, 6);
4197         immutable idate = Date(0, 7, 6);
4198         assert(date.yearBC == 1);
4199         assert(cdate.yearBC == 1);
4200         assert(idate.yearBC == 1);
4201     }
4202 
4203 
4204     /++
4205         Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4206 
4207         Params:
4208             year = The year B.C. to set this $(LREF Date)'s year to.
4209 
4210         Throws:
4211             $(REF DateTimeException,std,datetime,date) if a non-positive value
4212             is given.
4213      +/
4214     @property void yearBC(int year) @safe pure
4215     {
4216         if (year <= 0)
4217             throw new DateTimeException("The given year is not a year B.C.");
4218         _year = cast(short)((year - 1) * -1);
4219     }
4220 
4221     ///
4222     @safe unittest
4223     {
4224         auto date = Date(2010, 1, 1);
4225         date.yearBC = 1;
4226         assert(date == Date(0, 1, 1));
4227 
4228         date.yearBC = 10;
4229         assert(date == Date(-9, 1, 1));
4230     }
4231 
4232     @safe unittest
4233     {
4234         assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
4235 
4236         auto date = Date(0, 7, 6);
4237         const cdate = Date(0, 7, 6);
4238         immutable idate = Date(0, 7, 6);
4239         date.yearBC = 7;
4240         assert(date.yearBC == 7);
4241         static assert(!__traits(compiles, cdate.yearBC = 7));
4242         static assert(!__traits(compiles, idate.yearBC = 7));
4243     }
4244 
4245 
4246     /++
4247         Month of a Gregorian Year.
4248      +/
4249     @property Month month() const @safe pure nothrow @nogc
4250     {
4251         return _month;
4252     }
4253 
4254     ///
4255     @safe unittest
4256     {
4257         assert(Date(1999, 7, 6).month == 7);
4258         assert(Date(2010, 10, 4).month == 10);
4259         assert(Date(-7, 4, 5).month == 4);
4260     }
4261 
4262     @safe unittest
4263     {
4264         assert(Date.init.month == 1);
4265         assert(Date(1999, 7, 6).month == 7);
4266         assert(Date(-1999, 7, 6).month == 7);
4267 
4268         const cdate = Date(1999, 7, 6);
4269         immutable idate = Date(1999, 7, 6);
4270         assert(cdate.month == 7);
4271         assert(idate.month == 7);
4272     }
4273 
4274     /++
4275         Month of a Gregorian Year.
4276 
4277         Params:
4278             month = The month to set this $(LREF Date)'s month to.
4279 
4280         Throws:
4281             $(REF DateTimeException,std,datetime,date) if the given month is
4282             not a valid month or if the current day would not be valid in the
4283             given month.
4284      +/
4285     @property void month(Month month) @safe pure
4286     {
4287         enforceValid!"months"(month);
4288         enforceValid!"days"(_year, month, _day);
4289         _month = cast(Month) month;
4290     }
4291 
4292     @safe unittest
4293     {
4294         static void testDate(Date date, Month month, Date expected = Date.init)
4295         {
4296             date.month = month;
4297             assert(expected != Date.init);
4298             assert(date == expected);
4299         }
4300 
4301         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
4302         assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
4303         assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
4304         assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
4305 
4306         testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
4307         testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
4308 
4309         const cdate = Date(1999, 7, 6);
4310         immutable idate = Date(1999, 7, 6);
4311         static assert(!__traits(compiles, cdate.month = 7));
4312         static assert(!__traits(compiles, idate.month = 7));
4313     }
4314 
4315 
4316     /++
4317         Day of a Gregorian Month.
4318      +/
4319     @property ubyte day() const @safe pure nothrow @nogc
4320     {
4321         return _day;
4322     }
4323 
4324     ///
4325     @safe unittest
4326     {
4327         assert(Date(1999, 7, 6).day == 6);
4328         assert(Date(2010, 10, 4).day == 4);
4329         assert(Date(-7, 4, 5).day == 5);
4330     }
4331 
4332     @safe unittest
4333     {
4334         import std.format : format;
4335         import std.range : chain;
4336 
4337         static void test(Date date, int expected)
4338         {
4339             assert(date.day == expected, format("Value given: %s", date));
4340         }
4341 
4342         foreach (year; chain(testYearsBC, testYearsAD))
4343         {
4344             foreach (md; testMonthDays)
4345                 test(Date(year, md.month, md.day), md.day);
4346         }
4347 
4348         const cdate = Date(1999, 7, 6);
4349         immutable idate = Date(1999, 7, 6);
4350         assert(cdate.day == 6);
4351         assert(idate.day == 6);
4352     }
4353 
4354     /++
4355         Day of a Gregorian Month.
4356 
4357         Params:
4358             day = The day of the month to set this $(LREF Date)'s day to.
4359 
4360         Throws:
4361             $(REF DateTimeException,std,datetime,date) if the given day is not
4362             a valid day of the current month.
4363      +/
4364     @property void day(int day) @safe pure
4365     {
4366         enforceValid!"days"(_year, _month, day);
4367         _day = cast(ubyte) day;
4368     }
4369 
4370     @safe unittest
4371     {
4372         import std.exception : assertNotThrown;
4373 
4374         static void testDate(Date date, int day)
4375         {
4376             date.day = day;
4377         }
4378 
4379         // Test A.D.
4380         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
4381         assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
4382         assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
4383         assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
4384         assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
4385         assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
4386         assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
4387         assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
4388         assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
4389         assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
4390         assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
4391         assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
4392         assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
4393         assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
4394 
4395         assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
4396         assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
4397         assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
4398         assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
4399         assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
4400         assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
4401         assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
4402         assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
4403         assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
4404         assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
4405         assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
4406         assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
4407         assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
4408 
4409         {
4410             auto date = Date(1, 1, 1);
4411             date.day = 6;
4412             assert(date == Date(1, 1, 6));
4413         }
4414 
4415         // Test B.C.
4416         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
4417         assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
4418         assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
4419         assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
4420         assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
4421         assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
4422         assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
4423         assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
4424         assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
4425         assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
4426         assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
4427         assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
4428         assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
4429         assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
4430 
4431         assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
4432         assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
4433         assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
4434         assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
4435         assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
4436         assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
4437         assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
4438         assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
4439         assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
4440         assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
4441         assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
4442         assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
4443         assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
4444 
4445         {
4446             auto date = Date(-1, 1, 1);
4447             date.day = 6;
4448             assert(date == Date(-1, 1, 6));
4449         }
4450 
4451         const cdate = Date(1999, 7, 6);
4452         immutable idate = Date(1999, 7, 6);
4453         static assert(!__traits(compiles, cdate.day = 6));
4454         static assert(!__traits(compiles, idate.day = 6));
4455     }
4456 
4457 
4458     /++
4459         Adds the given number of years or months to this $(LREF Date), mutating
4460         it. A negative number will subtract.
4461 
4462         Note that if day overflow is allowed, and the date with the adjusted
4463         year/month overflows the number of days in the new month, then the month
4464         will be incremented by one, and the day set to the number of days
4465         overflowed. (e.g. if the day were 31 and the new month were June, then
4466         the month would be incremented to July, and the new day would be 1). If
4467         day overflow is not allowed, then the day will be set to the last valid
4468         day in the month (e.g. June 31st would become June 30th).
4469 
4470         Params:
4471             units         = The type of units to add ("years" or "months").
4472             value         = The number of months or years to add to this
4473                             $(LREF Date).
4474             allowOverflow = Whether the day should be allowed to overflow,
4475                             causing the month to increment.
4476 
4477         Returns:
4478             A reference to the `Date` (`this`).
4479       +/
4480     @safe pure nothrow @nogc
4481     ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4482         if (units == "years")
4483     {
4484         _year += value;
4485 
4486         if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
4487         {
4488             if (allowOverflow == AllowDayOverflow.yes)
4489             {
4490                 _month = Month.mar;
4491                 _day = 1;
4492             }
4493             else
4494                 _day = 28;
4495         }
4496 
4497         return this;
4498     }
4499 
4500     ///
4501     @safe unittest
4502     {
4503         auto d1 = Date(2010, 1, 1);
4504         d1.add!"months"(11);
4505         assert(d1 == Date(2010, 12, 1));
4506 
4507         auto d2 = Date(2010, 1, 1);
4508         d2.add!"months"(-11);
4509         assert(d2 == Date(2009, 2, 1));
4510 
4511         auto d3 = Date(2000, 2, 29);
4512         d3.add!"years"(1);
4513         assert(d3 == Date(2001, 3, 1));
4514 
4515         auto d4 = Date(2000, 2, 29);
4516         d4.add!"years"(1, AllowDayOverflow.no);
4517         assert(d4 == Date(2001, 2, 28));
4518     }
4519 
4520     // Test add!"years"() with AllowDayOverflow.yes
4521     @safe unittest
4522     {
4523         // Test A.D.
4524         {
4525             auto date = Date(1999, 7, 6);
4526             date.add!"years"(7);
4527             assert(date == Date(2006, 7, 6));
4528             date.add!"years"(-9);
4529             assert(date == Date(1997, 7, 6));
4530         }
4531 
4532         {
4533             auto date = Date(1999, 2, 28);
4534             date.add!"years"(1);
4535             assert(date == Date(2000, 2, 28));
4536         }
4537 
4538         {
4539             auto date = Date(2000, 2, 29);
4540             date.add!"years"(-1);
4541             assert(date == Date(1999, 3, 1));
4542         }
4543 
4544         // Test B.C.
4545         {
4546             auto date = Date(-1999, 7, 6);
4547             date.add!"years"(-7);
4548             assert(date == Date(-2006, 7, 6));
4549             date.add!"years"(9);
4550             assert(date == Date(-1997, 7, 6));
4551         }
4552 
4553         {
4554             auto date = Date(-1999, 2, 28);
4555             date.add!"years"(-1);
4556             assert(date == Date(-2000, 2, 28));
4557         }
4558 
4559         {
4560             auto date = Date(-2000, 2, 29);
4561             date.add!"years"(1);
4562             assert(date == Date(-1999, 3, 1));
4563         }
4564 
4565         // Test Both
4566         {
4567             auto date = Date(4, 7, 6);
4568             date.add!"years"(-5);
4569             assert(date == Date(-1, 7, 6));
4570             date.add!"years"(5);
4571             assert(date == Date(4, 7, 6));
4572         }
4573 
4574         {
4575             auto date = Date(-4, 7, 6);
4576             date.add!"years"(5);
4577             assert(date == Date(1, 7, 6));
4578             date.add!"years"(-5);
4579             assert(date == Date(-4, 7, 6));
4580         }
4581 
4582         {
4583             auto date = Date(4, 7, 6);
4584             date.add!"years"(-8);
4585             assert(date == Date(-4, 7, 6));
4586             date.add!"years"(8);
4587             assert(date == Date(4, 7, 6));
4588         }
4589 
4590         {
4591             auto date = Date(-4, 7, 6);
4592             date.add!"years"(8);
4593             assert(date == Date(4, 7, 6));
4594             date.add!"years"(-8);
4595             assert(date == Date(-4, 7, 6));
4596         }
4597 
4598         {
4599             auto date = Date(-4, 2, 29);
4600             date.add!"years"(5);
4601             assert(date == Date(1, 3, 1));
4602         }
4603 
4604         {
4605             auto date = Date(4, 2, 29);
4606             date.add!"years"(-5);
4607             assert(date == Date(-1, 3, 1));
4608         }
4609 
4610         {
4611             auto date = Date(4, 2, 29);
4612             date.add!"years"(-5).add!"years"(7);
4613             assert(date == Date(6, 3, 1));
4614         }
4615 
4616         const cdate = Date(1999, 7, 6);
4617         immutable idate = Date(1999, 7, 6);
4618         static assert(!__traits(compiles, cdate.add!"years"(7)));
4619         static assert(!__traits(compiles, idate.add!"years"(7)));
4620     }
4621 
4622     // Test add!"years"() with AllowDayOverflow.no
4623     @safe unittest
4624     {
4625         // Test A.D.
4626         {
4627             auto date = Date(1999, 7, 6);
4628             date.add!"years"(7, AllowDayOverflow.no);
4629             assert(date == Date(2006, 7, 6));
4630             date.add!"years"(-9, AllowDayOverflow.no);
4631             assert(date == Date(1997, 7, 6));
4632         }
4633 
4634         {
4635             auto date = Date(1999, 2, 28);
4636             date.add!"years"(1, AllowDayOverflow.no);
4637             assert(date == Date(2000, 2, 28));
4638         }
4639 
4640         {
4641             auto date = Date(2000, 2, 29);
4642             date.add!"years"(-1, AllowDayOverflow.no);
4643             assert(date == Date(1999, 2, 28));
4644         }
4645 
4646         // Test B.C.
4647         {
4648             auto date = Date(-1999, 7, 6);
4649             date.add!"years"(-7, AllowDayOverflow.no);
4650             assert(date == Date(-2006, 7, 6));
4651             date.add!"years"(9, AllowDayOverflow.no);
4652             assert(date == Date(-1997, 7, 6));
4653         }
4654 
4655         {
4656             auto date = Date(-1999, 2, 28);
4657             date.add!"years"(-1, AllowDayOverflow.no);
4658             assert(date == Date(-2000, 2, 28));
4659         }
4660 
4661         {
4662             auto date = Date(-2000, 2, 29);
4663             date.add!"years"(1, AllowDayOverflow.no);
4664             assert(date == Date(-1999, 2, 28));
4665         }
4666 
4667         // Test Both
4668         {
4669             auto date = Date(4, 7, 6);
4670             date.add!"years"(-5, AllowDayOverflow.no);
4671             assert(date == Date(-1, 7, 6));
4672             date.add!"years"(5, AllowDayOverflow.no);
4673             assert(date == Date(4, 7, 6));
4674         }
4675 
4676         {
4677             auto date = Date(-4, 7, 6);
4678             date.add!"years"(5, AllowDayOverflow.no);
4679             assert(date == Date(1, 7, 6));
4680             date.add!"years"(-5, AllowDayOverflow.no);
4681             assert(date == Date(-4, 7, 6));
4682         }
4683 
4684         {
4685             auto date = Date(4, 7, 6);
4686             date.add!"years"(-8, AllowDayOverflow.no);
4687             assert(date == Date(-4, 7, 6));
4688             date.add!"years"(8, AllowDayOverflow.no);
4689             assert(date == Date(4, 7, 6));
4690         }
4691 
4692         {
4693             auto date = Date(-4, 7, 6);
4694             date.add!"years"(8, AllowDayOverflow.no);
4695             assert(date == Date(4, 7, 6));
4696             date.add!"years"(-8, AllowDayOverflow.no);
4697             assert(date == Date(-4, 7, 6));
4698         }
4699 
4700         {
4701             auto date = Date(-4, 2, 29);
4702             date.add!"years"(5, AllowDayOverflow.no);
4703             assert(date == Date(1, 2, 28));
4704         }
4705 
4706         {
4707             auto date = Date(4, 2, 29);
4708             date.add!"years"(-5, AllowDayOverflow.no);
4709             assert(date == Date(-1, 2, 28));
4710         }
4711 
4712         {
4713             auto date = Date(4, 2, 29);
4714             date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
4715             assert(date == Date(6, 2, 28));
4716         }
4717     }
4718 
4719 
4720     // Shares documentation with "years" version.
4721     @safe pure nothrow @nogc
4722     ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4723         if (units == "months")
4724     {
4725         auto years = months / 12;
4726         months %= 12;
4727         auto newMonth = _month + months;
4728 
4729         if (months < 0)
4730         {
4731             if (newMonth < 1)
4732             {
4733                 newMonth += 12;
4734                 --years;
4735             }
4736         }
4737         else if (newMonth > 12)
4738         {
4739             newMonth -= 12;
4740             ++years;
4741         }
4742 
4743         _year += years;
4744         _month = cast(Month) newMonth;
4745 
4746         immutable currMaxDay = maxDay(_year, _month);
4747         immutable overflow = _day - currMaxDay;
4748 
4749         if (overflow > 0)
4750         {
4751             if (allowOverflow == AllowDayOverflow.yes)
4752             {
4753                 ++_month;
4754                 _day = cast(ubyte) overflow;
4755             }
4756             else
4757                 _day = cast(ubyte) currMaxDay;
4758         }
4759 
4760         return this;
4761     }
4762 
4763     // Test add!"months"() with AllowDayOverflow.yes
4764     @safe unittest
4765     {
4766         // Test A.D.
4767         {
4768             auto date = Date(1999, 7, 6);
4769             date.add!"months"(3);
4770             assert(date == Date(1999, 10, 6));
4771             date.add!"months"(-4);
4772             assert(date == Date(1999, 6, 6));
4773         }
4774 
4775         {
4776             auto date = Date(1999, 7, 6);
4777             date.add!"months"(6);
4778             assert(date == Date(2000, 1, 6));
4779             date.add!"months"(-6);
4780             assert(date == Date(1999, 7, 6));
4781         }
4782 
4783         {
4784             auto date = Date(1999, 7, 6);
4785             date.add!"months"(27);
4786             assert(date == Date(2001, 10, 6));
4787             date.add!"months"(-28);
4788             assert(date == Date(1999, 6, 6));
4789         }
4790 
4791         {
4792             auto date = Date(1999, 5, 31);
4793             date.add!"months"(1);
4794             assert(date == Date(1999, 7, 1));
4795         }
4796 
4797         {
4798             auto date = Date(1999, 5, 31);
4799             date.add!"months"(-1);
4800             assert(date == Date(1999, 5, 1));
4801         }
4802 
4803         {
4804             auto date = Date(1999, 2, 28);
4805             date.add!"months"(12);
4806             assert(date == Date(2000, 2, 28));
4807         }
4808 
4809         {
4810             auto date = Date(2000, 2, 29);
4811             date.add!"months"(12);
4812             assert(date == Date(2001, 3, 1));
4813         }
4814 
4815         {
4816             auto date = Date(1999, 7, 31);
4817             date.add!"months"(1);
4818             assert(date == Date(1999, 8, 31));
4819             date.add!"months"(1);
4820             assert(date == Date(1999, 10, 1));
4821         }
4822 
4823         {
4824             auto date = Date(1998, 8, 31);
4825             date.add!"months"(13);
4826             assert(date == Date(1999, 10, 1));
4827             date.add!"months"(-13);
4828             assert(date == Date(1998, 9, 1));
4829         }
4830 
4831         {
4832             auto date = Date(1997, 12, 31);
4833             date.add!"months"(13);
4834             assert(date == Date(1999, 1, 31));
4835             date.add!"months"(-13);
4836             assert(date == Date(1997, 12, 31));
4837         }
4838 
4839         {
4840             auto date = Date(1997, 12, 31);
4841             date.add!"months"(14);
4842             assert(date == Date(1999, 3, 3));
4843             date.add!"months"(-14);
4844             assert(date == Date(1998, 1, 3));
4845         }
4846 
4847         {
4848             auto date = Date(1998, 12, 31);
4849             date.add!"months"(14);
4850             assert(date == Date(2000, 3, 2));
4851             date.add!"months"(-14);
4852             assert(date == Date(1999, 1, 2));
4853         }
4854 
4855         {
4856             auto date = Date(1999, 12, 31);
4857             date.add!"months"(14);
4858             assert(date == Date(2001, 3, 3));
4859             date.add!"months"(-14);
4860             assert(date == Date(2000, 1, 3));
4861         }
4862 
4863         // Test B.C.
4864         {
4865             auto date = Date(-1999, 7, 6);
4866             date.add!"months"(3);
4867             assert(date == Date(-1999, 10, 6));
4868             date.add!"months"(-4);
4869             assert(date == Date(-1999, 6, 6));
4870         }
4871 
4872         {
4873             auto date = Date(-1999, 7, 6);
4874             date.add!"months"(6);
4875             assert(date == Date(-1998, 1, 6));
4876             date.add!"months"(-6);
4877             assert(date == Date(-1999, 7, 6));
4878         }
4879 
4880         {
4881             auto date = Date(-1999, 7, 6);
4882             date.add!"months"(-27);
4883             assert(date == Date(-2001, 4, 6));
4884             date.add!"months"(28);
4885             assert(date == Date(-1999, 8, 6));
4886         }
4887 
4888         {
4889             auto date = Date(-1999, 5, 31);
4890             date.add!"months"(1);
4891             assert(date == Date(-1999, 7, 1));
4892         }
4893 
4894         {
4895             auto date = Date(-1999, 5, 31);
4896             date.add!"months"(-1);
4897             assert(date == Date(-1999, 5, 1));
4898         }
4899 
4900         {
4901             auto date = Date(-1999, 2, 28);
4902             date.add!"months"(-12);
4903             assert(date == Date(-2000, 2, 28));
4904         }
4905 
4906         {
4907             auto date = Date(-2000, 2, 29);
4908             date.add!"months"(-12);
4909             assert(date == Date(-2001, 3, 1));
4910         }
4911 
4912         {
4913             auto date = Date(-1999, 7, 31);
4914             date.add!"months"(1);
4915             assert(date == Date(-1999, 8, 31));
4916             date.add!"months"(1);
4917             assert(date == Date(-1999, 10, 1));
4918         }
4919 
4920         {
4921             auto date = Date(-1998, 8, 31);
4922             date.add!"months"(13);
4923             assert(date == Date(-1997, 10, 1));
4924             date.add!"months"(-13);
4925             assert(date == Date(-1998, 9, 1));
4926         }
4927 
4928         {
4929             auto date = Date(-1997, 12, 31);
4930             date.add!"months"(13);
4931             assert(date == Date(-1995, 1, 31));
4932             date.add!"months"(-13);
4933             assert(date == Date(-1997, 12, 31));
4934         }
4935 
4936         {
4937             auto date = Date(-1997, 12, 31);
4938             date.add!"months"(14);
4939             assert(date == Date(-1995, 3, 3));
4940             date.add!"months"(-14);
4941             assert(date == Date(-1996, 1, 3));
4942         }
4943 
4944         {
4945             auto date = Date(-2002, 12, 31);
4946             date.add!"months"(14);
4947             assert(date == Date(-2000, 3, 2));
4948             date.add!"months"(-14);
4949             assert(date == Date(-2001, 1, 2));
4950         }
4951 
4952         {
4953             auto date = Date(-2001, 12, 31);
4954             date.add!"months"(14);
4955             assert(date == Date(-1999, 3, 3));
4956             date.add!"months"(-14);
4957             assert(date == Date(-2000, 1, 3));
4958         }
4959 
4960         // Test Both
4961         {
4962             auto date = Date(1, 1, 1);
4963             date.add!"months"(-1);
4964             assert(date == Date(0, 12, 1));
4965             date.add!"months"(1);
4966             assert(date == Date(1, 1, 1));
4967         }
4968 
4969         {
4970             auto date = Date(4, 1, 1);
4971             date.add!"months"(-48);
4972             assert(date == Date(0, 1, 1));
4973             date.add!"months"(48);
4974             assert(date == Date(4, 1, 1));
4975         }
4976 
4977         {
4978             auto date = Date(4, 3, 31);
4979             date.add!"months"(-49);
4980             assert(date == Date(0, 3, 2));
4981             date.add!"months"(49);
4982             assert(date == Date(4, 4, 2));
4983         }
4984 
4985         {
4986             auto date = Date(4, 3, 31);
4987             date.add!"months"(-85);
4988             assert(date == Date(-3, 3, 3));
4989             date.add!"months"(85);
4990             assert(date == Date(4, 4, 3));
4991         }
4992 
4993         {
4994             auto date = Date(-3, 3, 31);
4995             date.add!"months"(85).add!"months"(-83);
4996             assert(date == Date(-3, 6, 1));
4997         }
4998 
4999         const cdate = Date(1999, 7, 6);
5000         immutable idate = Date(1999, 7, 6);
5001         static assert(!__traits(compiles, cdate.add!"months"(3)));
5002         static assert(!__traits(compiles, idate.add!"months"(3)));
5003     }
5004 
5005     // Test add!"months"() with AllowDayOverflow.no
5006     @safe unittest
5007     {
5008         // Test A.D.
5009         {
5010             auto date = Date(1999, 7, 6);
5011             date.add!"months"(3, AllowDayOverflow.no);
5012             assert(date == Date(1999, 10, 6));
5013             date.add!"months"(-4, AllowDayOverflow.no);
5014             assert(date == Date(1999, 6, 6));
5015         }
5016 
5017         {
5018             auto date = Date(1999, 7, 6);
5019             date.add!"months"(6, AllowDayOverflow.no);
5020             assert(date == Date(2000, 1, 6));
5021             date.add!"months"(-6, AllowDayOverflow.no);
5022             assert(date == Date(1999, 7, 6));
5023         }
5024 
5025         {
5026             auto date = Date(1999, 7, 6);
5027             date.add!"months"(27, AllowDayOverflow.no);
5028             assert(date == Date(2001, 10, 6));
5029             date.add!"months"(-28, AllowDayOverflow.no);
5030             assert(date == Date(1999, 6, 6));
5031         }
5032 
5033         {
5034             auto date = Date(1999, 5, 31);
5035             date.add!"months"(1, AllowDayOverflow.no);
5036             assert(date == Date(1999, 6, 30));
5037         }
5038 
5039         {
5040             auto date = Date(1999, 5, 31);
5041             date.add!"months"(-1, AllowDayOverflow.no);
5042             assert(date == Date(1999, 4, 30));
5043         }
5044 
5045         {
5046             auto date = Date(1999, 2, 28);
5047             date.add!"months"(12, AllowDayOverflow.no);
5048             assert(date == Date(2000, 2, 28));
5049         }
5050 
5051         {
5052             auto date = Date(2000, 2, 29);
5053             date.add!"months"(12, AllowDayOverflow.no);
5054             assert(date == Date(2001, 2, 28));
5055         }
5056 
5057         {
5058             auto date = Date(1999, 7, 31);
5059             date.add!"months"(1, AllowDayOverflow.no);
5060             assert(date == Date(1999, 8, 31));
5061             date.add!"months"(1, AllowDayOverflow.no);
5062             assert(date == Date(1999, 9, 30));
5063         }
5064 
5065         {
5066             auto date = Date(1998, 8, 31);
5067             date.add!"months"(13, AllowDayOverflow.no);
5068             assert(date == Date(1999, 9, 30));
5069             date.add!"months"(-13, AllowDayOverflow.no);
5070             assert(date == Date(1998, 8, 30));
5071         }
5072 
5073         {
5074             auto date = Date(1997, 12, 31);
5075             date.add!"months"(13, AllowDayOverflow.no);
5076             assert(date == Date(1999, 1, 31));
5077             date.add!"months"(-13, AllowDayOverflow.no);
5078             assert(date == Date(1997, 12, 31));
5079         }
5080 
5081         {
5082             auto date = Date(1997, 12, 31);
5083             date.add!"months"(14, AllowDayOverflow.no);
5084             assert(date == Date(1999, 2, 28));
5085             date.add!"months"(-14, AllowDayOverflow.no);
5086             assert(date == Date(1997, 12, 28));
5087         }
5088 
5089         {
5090             auto date = Date(1998, 12, 31);
5091             date.add!"months"(14, AllowDayOverflow.no);
5092             assert(date == Date(2000, 2, 29));
5093             date.add!"months"(-14, AllowDayOverflow.no);
5094             assert(date == Date(1998, 12, 29));
5095         }
5096 
5097         {
5098             auto date = Date(1999, 12, 31);
5099             date.add!"months"(14, AllowDayOverflow.no);
5100             assert(date == Date(2001, 2, 28));
5101             date.add!"months"(-14, AllowDayOverflow.no);
5102             assert(date == Date(1999, 12, 28));
5103         }
5104 
5105         // Test B.C.
5106         {
5107             auto date = Date(-1999, 7, 6);
5108             date.add!"months"(3, AllowDayOverflow.no);
5109             assert(date == Date(-1999, 10, 6));
5110             date.add!"months"(-4, AllowDayOverflow.no);
5111             assert(date == Date(-1999, 6, 6));
5112         }
5113 
5114         {
5115             auto date = Date(-1999, 7, 6);
5116             date.add!"months"(6, AllowDayOverflow.no);
5117             assert(date == Date(-1998, 1, 6));
5118             date.add!"months"(-6, AllowDayOverflow.no);
5119             assert(date == Date(-1999, 7, 6));
5120         }
5121 
5122         {
5123             auto date = Date(-1999, 7, 6);
5124             date.add!"months"(-27, AllowDayOverflow.no);
5125             assert(date == Date(-2001, 4, 6));
5126             date.add!"months"(28, AllowDayOverflow.no);
5127             assert(date == Date(-1999, 8, 6));
5128         }
5129 
5130         {
5131             auto date = Date(-1999, 5, 31);
5132             date.add!"months"(1, AllowDayOverflow.no);
5133             assert(date == Date(-1999, 6, 30));
5134         }
5135 
5136         {
5137             auto date = Date(-1999, 5, 31);
5138             date.add!"months"(-1, AllowDayOverflow.no);
5139             assert(date == Date(-1999, 4, 30));
5140         }
5141 
5142         {
5143             auto date = Date(-1999, 2, 28);
5144             date.add!"months"(-12, AllowDayOverflow.no);
5145             assert(date == Date(-2000, 2, 28));
5146         }
5147 
5148         {
5149             auto date = Date(-2000, 2, 29);
5150             date.add!"months"(-12, AllowDayOverflow.no);
5151             assert(date == Date(-2001, 2, 28));
5152         }
5153 
5154         {
5155             auto date = Date(-1999, 7, 31);
5156             date.add!"months"(1, AllowDayOverflow.no);
5157             assert(date == Date(-1999, 8, 31));
5158             date.add!"months"(1, AllowDayOverflow.no);
5159             assert(date == Date(-1999, 9, 30));
5160         }
5161 
5162         {
5163             auto date = Date(-1998, 8, 31);
5164             date.add!"months"(13, AllowDayOverflow.no);
5165             assert(date == Date(-1997, 9, 30));
5166             date.add!"months"(-13, AllowDayOverflow.no);
5167             assert(date == Date(-1998, 8, 30));
5168         }
5169 
5170         {
5171             auto date = Date(-1997, 12, 31);
5172             date.add!"months"(13, AllowDayOverflow.no);
5173             assert(date == Date(-1995, 1, 31));
5174             date.add!"months"(-13, AllowDayOverflow.no);
5175             assert(date == Date(-1997, 12, 31));
5176         }
5177 
5178         {
5179             auto date = Date(-1997, 12, 31);
5180             date.add!"months"(14, AllowDayOverflow.no);
5181             assert(date == Date(-1995, 2, 28));
5182             date.add!"months"(-14, AllowDayOverflow.no);
5183             assert(date == Date(-1997, 12, 28));
5184         }
5185 
5186         {
5187             auto date = Date(-2002, 12, 31);
5188             date.add!"months"(14, AllowDayOverflow.no);
5189             assert(date == Date(-2000, 2, 29));
5190             date.add!"months"(-14, AllowDayOverflow.no);
5191             assert(date == Date(-2002, 12, 29));
5192         }
5193 
5194         {
5195             auto date = Date(-2001, 12, 31);
5196             date.add!"months"(14, AllowDayOverflow.no);
5197             assert(date == Date(-1999, 2, 28));
5198             date.add!"months"(-14, AllowDayOverflow.no);
5199             assert(date == Date(-2001, 12, 28));
5200         }
5201 
5202         // Test Both
5203         {
5204             auto date = Date(1, 1, 1);
5205             date.add!"months"(-1, AllowDayOverflow.no);
5206             assert(date == Date(0, 12, 1));
5207             date.add!"months"(1, AllowDayOverflow.no);
5208             assert(date == Date(1, 1, 1));
5209         }
5210 
5211         {
5212             auto date = Date(4, 1, 1);
5213             date.add!"months"(-48, AllowDayOverflow.no);
5214             assert(date == Date(0, 1, 1));
5215             date.add!"months"(48, AllowDayOverflow.no);
5216             assert(date == Date(4, 1, 1));
5217         }
5218 
5219         {
5220             auto date = Date(4, 3, 31);
5221             date.add!"months"(-49, AllowDayOverflow.no);
5222             assert(date == Date(0, 2, 29));
5223             date.add!"months"(49, AllowDayOverflow.no);
5224             assert(date == Date(4, 3, 29));
5225         }
5226 
5227         {
5228             auto date = Date(4, 3, 31);
5229             date.add!"months"(-85, AllowDayOverflow.no);
5230             assert(date == Date(-3, 2, 28));
5231             date.add!"months"(85, AllowDayOverflow.no);
5232             assert(date == Date(4, 3, 28));
5233         }
5234 
5235         {
5236             auto date = Date(-3, 3, 31);
5237             date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
5238             assert(date == Date(-3, 5, 30));
5239         }
5240     }
5241 
5242 
5243     /++
5244         Adds the given number of years or months to this $(LREF Date), mutating
5245         it. A negative number will subtract.
5246 
5247         The difference between rolling and adding is that rolling does not
5248         affect larger units. Rolling a $(LREF Date) 12 months gets
5249         the exact same $(LREF Date). However, the days can still be affected due
5250         to the differing number of days in each month.
5251 
5252         Because there are no units larger than years, there is no difference
5253         between adding and rolling years.
5254 
5255         Params:
5256             units         = The type of units to add ("years" or "months").
5257             value         = The number of months or years to add to this
5258                             $(LREF Date).
5259             allowOverflow = Whether the day should be allowed to overflow,
5260                             causing the month to increment.
5261 
5262         Returns:
5263             A reference to the `Date` (`this`).
5264       +/
5265     @safe pure nothrow @nogc
5266     ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5267         if (units == "years")
5268     {
5269         return add!"years"(value, allowOverflow);
5270     }
5271 
5272     ///
5273     @safe unittest
5274     {
5275         auto d1 = Date(2010, 1, 1);
5276         d1.roll!"months"(1);
5277         assert(d1 == Date(2010, 2, 1));
5278 
5279         auto d2 = Date(2010, 1, 1);
5280         d2.roll!"months"(-1);
5281         assert(d2 == Date(2010, 12, 1));
5282 
5283         auto d3 = Date(1999, 1, 29);
5284         d3.roll!"months"(1);
5285         assert(d3 == Date(1999, 3, 1));
5286 
5287         auto d4 = Date(1999, 1, 29);
5288         d4.roll!"months"(1, AllowDayOverflow.no);
5289         assert(d4 == Date(1999, 2, 28));
5290 
5291         auto d5 = Date(2000, 2, 29);
5292         d5.roll!"years"(1);
5293         assert(d5 == Date(2001, 3, 1));
5294 
5295         auto d6 = Date(2000, 2, 29);
5296         d6.roll!"years"(1, AllowDayOverflow.no);
5297         assert(d6 == Date(2001, 2, 28));
5298     }
5299 
5300     @safe unittest
5301     {
5302         const cdate = Date(1999, 7, 6);
5303         immutable idate = Date(1999, 7, 6);
5304         static assert(!__traits(compiles, cdate.roll!"years"(3)));
5305         static assert(!__traits(compiles, idate.rolYears(3)));
5306     }
5307 
5308 
5309     // Shares documentation with "years" version.
5310     @safe pure nothrow @nogc
5311     ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5312         if (units == "months")
5313     {
5314         months %= 12;
5315         auto newMonth = _month + months;
5316 
5317         if (months < 0)
5318         {
5319             if (newMonth < 1)
5320                 newMonth += 12;
5321         }
5322         else
5323         {
5324             if (newMonth > 12)
5325                 newMonth -= 12;
5326         }
5327 
5328         _month = cast(Month) newMonth;
5329 
5330         immutable currMaxDay = maxDay(_year, _month);
5331         immutable overflow = _day - currMaxDay;
5332 
5333         if (overflow > 0)
5334         {
5335             if (allowOverflow == AllowDayOverflow.yes)
5336             {
5337                 ++_month;
5338                 _day = cast(ubyte) overflow;
5339             }
5340             else
5341                 _day = cast(ubyte) currMaxDay;
5342         }
5343 
5344         return this;
5345     }
5346 
5347     // Test roll!"months"() with AllowDayOverflow.yes
5348     @safe unittest
5349     {
5350         // Test A.D.
5351         {
5352             auto date = Date(1999, 7, 6);
5353             date.roll!"months"(3);
5354             assert(date == Date(1999, 10, 6));
5355             date.roll!"months"(-4);
5356             assert(date == Date(1999, 6, 6));
5357         }
5358 
5359         {
5360             auto date = Date(1999, 7, 6);
5361             date.roll!"months"(6);
5362             assert(date == Date(1999, 1, 6));
5363             date.roll!"months"(-6);
5364             assert(date == Date(1999, 7, 6));
5365         }
5366 
5367         {
5368             auto date = Date(1999, 7, 6);
5369             date.roll!"months"(27);
5370             assert(date == Date(1999, 10, 6));
5371             date.roll!"months"(-28);
5372             assert(date == Date(1999, 6, 6));
5373         }
5374 
5375         {
5376             auto date = Date(1999, 5, 31);
5377             date.roll!"months"(1);
5378             assert(date == Date(1999, 7, 1));
5379         }
5380 
5381         {
5382             auto date = Date(1999, 5, 31);
5383             date.roll!"months"(-1);
5384             assert(date == Date(1999, 5, 1));
5385         }
5386 
5387         {
5388             auto date = Date(1999, 2, 28);
5389             date.roll!"months"(12);
5390             assert(date == Date(1999, 2, 28));
5391         }
5392 
5393         {
5394             auto date = Date(2000, 2, 29);
5395             date.roll!"months"(12);
5396             assert(date == Date(2000, 2, 29));
5397         }
5398 
5399         {
5400             auto date = Date(1999, 7, 31);
5401             date.roll!"months"(1);
5402             assert(date == Date(1999, 8, 31));
5403             date.roll!"months"(1);
5404             assert(date == Date(1999, 10, 1));
5405         }
5406 
5407         {
5408             auto date = Date(1998, 8, 31);
5409             date.roll!"months"(13);
5410             assert(date == Date(1998, 10, 1));
5411             date.roll!"months"(-13);
5412             assert(date == Date(1998, 9, 1));
5413         }
5414 
5415         {
5416             auto date = Date(1997, 12, 31);
5417             date.roll!"months"(13);
5418             assert(date == Date(1997, 1, 31));
5419             date.roll!"months"(-13);
5420             assert(date == Date(1997, 12, 31));
5421         }
5422 
5423         {
5424             auto date = Date(1997, 12, 31);
5425             date.roll!"months"(14);
5426             assert(date == Date(1997, 3, 3));
5427             date.roll!"months"(-14);
5428             assert(date == Date(1997, 1, 3));
5429         }
5430 
5431         {
5432             auto date = Date(1998, 12, 31);
5433             date.roll!"months"(14);
5434             assert(date == Date(1998, 3, 3));
5435             date.roll!"months"(-14);
5436             assert(date == Date(1998, 1, 3));
5437         }
5438 
5439         {
5440             auto date = Date(1999, 12, 31);
5441             date.roll!"months"(14);
5442             assert(date == Date(1999, 3, 3));
5443             date.roll!"months"(-14);
5444             assert(date == Date(1999, 1, 3));
5445         }
5446 
5447         // Test B.C.
5448         {
5449             auto date = Date(-1999, 7, 6);
5450             date.roll!"months"(3);
5451             assert(date == Date(-1999, 10, 6));
5452             date.roll!"months"(-4);
5453             assert(date == Date(-1999, 6, 6));
5454         }
5455 
5456         {
5457             auto date = Date(-1999, 7, 6);
5458             date.roll!"months"(6);
5459             assert(date == Date(-1999, 1, 6));
5460             date.roll!"months"(-6);
5461             assert(date == Date(-1999, 7, 6));
5462         }
5463 
5464         {
5465             auto date = Date(-1999, 7, 6);
5466             date.roll!"months"(-27);
5467             assert(date == Date(-1999, 4, 6));
5468             date.roll!"months"(28);
5469             assert(date == Date(-1999, 8, 6));
5470         }
5471 
5472         {
5473             auto date = Date(-1999, 5, 31);
5474             date.roll!"months"(1);
5475             assert(date == Date(-1999, 7, 1));
5476         }
5477 
5478         {
5479             auto date = Date(-1999, 5, 31);
5480             date.roll!"months"(-1);
5481             assert(date == Date(-1999, 5, 1));
5482         }
5483 
5484         {
5485             auto date = Date(-1999, 2, 28);
5486             date.roll!"months"(-12);
5487             assert(date == Date(-1999, 2, 28));
5488         }
5489 
5490         {
5491             auto date = Date(-2000, 2, 29);
5492             date.roll!"months"(-12);
5493             assert(date == Date(-2000, 2, 29));
5494         }
5495 
5496         {
5497             auto date = Date(-1999, 7, 31);
5498             date.roll!"months"(1);
5499             assert(date == Date(-1999, 8, 31));
5500             date.roll!"months"(1);
5501             assert(date == Date(-1999, 10, 1));
5502         }
5503 
5504         {
5505             auto date = Date(-1998, 8, 31);
5506             date.roll!"months"(13);
5507             assert(date == Date(-1998, 10, 1));
5508             date.roll!"months"(-13);
5509             assert(date == Date(-1998, 9, 1));
5510         }
5511 
5512         {
5513             auto date = Date(-1997, 12, 31);
5514             date.roll!"months"(13);
5515             assert(date == Date(-1997, 1, 31));
5516             date.roll!"months"(-13);
5517             assert(date == Date(-1997, 12, 31));
5518         }
5519 
5520         {
5521             auto date = Date(-1997, 12, 31);
5522             date.roll!"months"(14);
5523             assert(date == Date(-1997, 3, 3));
5524             date.roll!"months"(-14);
5525             assert(date == Date(-1997, 1, 3));
5526         }
5527 
5528         {
5529             auto date = Date(-2002, 12, 31);
5530             date.roll!"months"(14);
5531             assert(date == Date(-2002, 3, 3));
5532             date.roll!"months"(-14);
5533             assert(date == Date(-2002, 1, 3));
5534         }
5535 
5536         {
5537             auto date = Date(-2001, 12, 31);
5538             date.roll!"months"(14);
5539             assert(date == Date(-2001, 3, 3));
5540             date.roll!"months"(-14);
5541             assert(date == Date(-2001, 1, 3));
5542         }
5543 
5544         // Test Both
5545         {
5546             auto date = Date(1, 1, 1);
5547             date.roll!"months"(-1);
5548             assert(date == Date(1, 12, 1));
5549             date.roll!"months"(1);
5550             assert(date == Date(1, 1, 1));
5551         }
5552 
5553         {
5554             auto date = Date(4, 1, 1);
5555             date.roll!"months"(-48);
5556             assert(date == Date(4, 1, 1));
5557             date.roll!"months"(48);
5558             assert(date == Date(4, 1, 1));
5559         }
5560 
5561         {
5562             auto date = Date(4, 3, 31);
5563             date.roll!"months"(-49);
5564             assert(date == Date(4, 3, 2));
5565             date.roll!"months"(49);
5566             assert(date == Date(4, 4, 2));
5567         }
5568 
5569         {
5570             auto date = Date(4, 3, 31);
5571             date.roll!"months"(-85);
5572             assert(date == Date(4, 3, 2));
5573             date.roll!"months"(85);
5574             assert(date == Date(4, 4, 2));
5575         }
5576 
5577         {
5578             auto date = Date(-1, 1, 1);
5579             date.roll!"months"(-1);
5580             assert(date == Date(-1, 12, 1));
5581             date.roll!"months"(1);
5582             assert(date == Date(-1, 1, 1));
5583         }
5584 
5585         {
5586             auto date = Date(-4, 1, 1);
5587             date.roll!"months"(-48);
5588             assert(date == Date(-4, 1, 1));
5589             date.roll!"months"(48);
5590             assert(date == Date(-4, 1, 1));
5591         }
5592 
5593         {
5594             auto date = Date(-4, 3, 31);
5595             date.roll!"months"(-49);
5596             assert(date == Date(-4, 3, 2));
5597             date.roll!"months"(49);
5598             assert(date == Date(-4, 4, 2));
5599         }
5600 
5601         {
5602             auto date = Date(-4, 3, 31);
5603             date.roll!"months"(-85);
5604             assert(date == Date(-4, 3, 2));
5605             date.roll!"months"(85);
5606             assert(date == Date(-4, 4, 2));
5607         }
5608 
5609         {
5610             auto date = Date(-3, 3, 31);
5611             date.roll!"months"(85).roll!"months"(-83);
5612             assert(date == Date(-3, 6, 1));
5613         }
5614 
5615         const cdate = Date(1999, 7, 6);
5616         immutable idate = Date(1999, 7, 6);
5617         static assert(!__traits(compiles, cdate.roll!"months"(3)));
5618         static assert(!__traits(compiles, idate.roll!"months"(3)));
5619     }
5620 
5621     // Test roll!"months"() with AllowDayOverflow.no
5622     @safe unittest
5623     {
5624         // Test A.D.
5625         {
5626             auto date = Date(1999, 7, 6);
5627             date.roll!"months"(3, AllowDayOverflow.no);
5628             assert(date == Date(1999, 10, 6));
5629             date.roll!"months"(-4, AllowDayOverflow.no);
5630             assert(date == Date(1999, 6, 6));
5631         }
5632 
5633         {
5634             auto date = Date(1999, 7, 6);
5635             date.roll!"months"(6, AllowDayOverflow.no);
5636             assert(date == Date(1999, 1, 6));
5637             date.roll!"months"(-6, AllowDayOverflow.no);
5638             assert(date == Date(1999, 7, 6));
5639         }
5640 
5641         {
5642             auto date = Date(1999, 7, 6);
5643             date.roll!"months"(27, AllowDayOverflow.no);
5644             assert(date == Date(1999, 10, 6));
5645             date.roll!"months"(-28, AllowDayOverflow.no);
5646             assert(date == Date(1999, 6, 6));
5647         }
5648 
5649         {
5650             auto date = Date(1999, 5, 31);
5651             date.roll!"months"(1, AllowDayOverflow.no);
5652             assert(date == Date(1999, 6, 30));
5653         }
5654 
5655         {
5656             auto date = Date(1999, 5, 31);
5657             date.roll!"months"(-1, AllowDayOverflow.no);
5658             assert(date == Date(1999, 4, 30));
5659         }
5660 
5661         {
5662             auto date = Date(1999, 2, 28);
5663             date.roll!"months"(12, AllowDayOverflow.no);
5664             assert(date == Date(1999, 2, 28));
5665         }
5666 
5667         {
5668             auto date = Date(2000, 2, 29);
5669             date.roll!"months"(12, AllowDayOverflow.no);
5670             assert(date == Date(2000, 2, 29));
5671         }
5672 
5673         {
5674             auto date = Date(1999, 7, 31);
5675             date.roll!"months"(1, AllowDayOverflow.no);
5676             assert(date == Date(1999, 8, 31));
5677             date.roll!"months"(1, AllowDayOverflow.no);
5678             assert(date == Date(1999, 9, 30));
5679         }
5680 
5681         {
5682             auto date = Date(1998, 8, 31);
5683             date.roll!"months"(13, AllowDayOverflow.no);
5684             assert(date == Date(1998, 9, 30));
5685             date.roll!"months"(-13, AllowDayOverflow.no);
5686             assert(date == Date(1998, 8, 30));
5687         }
5688 
5689         {
5690             auto date = Date(1997, 12, 31);
5691             date.roll!"months"(13, AllowDayOverflow.no);
5692             assert(date == Date(1997, 1, 31));
5693             date.roll!"months"(-13, AllowDayOverflow.no);
5694             assert(date == Date(1997, 12, 31));
5695         }
5696 
5697         {
5698             auto date = Date(1997, 12, 31);
5699             date.roll!"months"(14, AllowDayOverflow.no);
5700             assert(date == Date(1997, 2, 28));
5701             date.roll!"months"(-14, AllowDayOverflow.no);
5702             assert(date == Date(1997, 12, 28));
5703         }
5704 
5705         {
5706             auto date = Date(1998, 12, 31);
5707             date.roll!"months"(14, AllowDayOverflow.no);
5708             assert(date == Date(1998, 2, 28));
5709             date.roll!"months"(-14, AllowDayOverflow.no);
5710             assert(date == Date(1998, 12, 28));
5711         }
5712 
5713         {
5714             auto date = Date(1999, 12, 31);
5715             date.roll!"months"(14, AllowDayOverflow.no);
5716             assert(date == Date(1999, 2, 28));
5717             date.roll!"months"(-14, AllowDayOverflow.no);
5718             assert(date == Date(1999, 12, 28));
5719         }
5720 
5721         // Test B.C.
5722         {
5723             auto date = Date(-1999, 7, 6);
5724             date.roll!"months"(3, AllowDayOverflow.no);
5725             assert(date == Date(-1999, 10, 6));
5726             date.roll!"months"(-4, AllowDayOverflow.no);
5727             assert(date == Date(-1999, 6, 6));
5728         }
5729 
5730         {
5731             auto date = Date(-1999, 7, 6);
5732             date.roll!"months"(6, AllowDayOverflow.no);
5733             assert(date == Date(-1999, 1, 6));
5734             date.roll!"months"(-6, AllowDayOverflow.no);
5735             assert(date == Date(-1999, 7, 6));
5736         }
5737 
5738         {
5739             auto date = Date(-1999, 7, 6);
5740             date.roll!"months"(-27, AllowDayOverflow.no);
5741             assert(date == Date(-1999, 4, 6));
5742             date.roll!"months"(28, AllowDayOverflow.no);
5743             assert(date == Date(-1999, 8, 6));
5744         }
5745 
5746         {
5747             auto date = Date(-1999, 5, 31);
5748             date.roll!"months"(1, AllowDayOverflow.no);
5749             assert(date == Date(-1999, 6, 30));
5750         }
5751 
5752         {
5753             auto date = Date(-1999, 5, 31);
5754             date.roll!"months"(-1, AllowDayOverflow.no);
5755             assert(date == Date(-1999, 4, 30));
5756         }
5757 
5758         {
5759             auto date = Date(-1999, 2, 28);
5760             date.roll!"months"(-12, AllowDayOverflow.no);
5761             assert(date == Date(-1999, 2, 28));
5762         }
5763 
5764         {
5765             auto date = Date(-2000, 2, 29);
5766             date.roll!"months"(-12, AllowDayOverflow.no);
5767             assert(date == Date(-2000, 2, 29));
5768         }
5769 
5770         {
5771             auto date = Date(-1999, 7, 31);
5772             date.roll!"months"(1, AllowDayOverflow.no);
5773             assert(date == Date(-1999, 8, 31));
5774             date.roll!"months"(1, AllowDayOverflow.no);
5775             assert(date == Date(-1999, 9, 30));
5776         }
5777 
5778         {
5779             auto date = Date(-1998, 8, 31);
5780             date.roll!"months"(13, AllowDayOverflow.no);
5781             assert(date == Date(-1998, 9, 30));
5782             date.roll!"months"(-13, AllowDayOverflow.no);
5783             assert(date == Date(-1998, 8, 30));
5784         }
5785 
5786         {
5787             auto date = Date(-1997, 12, 31);
5788             date.roll!"months"(13, AllowDayOverflow.no);
5789             assert(date == Date(-1997, 1, 31));
5790             date.roll!"months"(-13, AllowDayOverflow.no);
5791             assert(date == Date(-1997, 12, 31));
5792         }
5793 
5794         {
5795             auto date = Date(-1997, 12, 31);
5796             date.roll!"months"(14, AllowDayOverflow.no);
5797             assert(date == Date(-1997, 2, 28));
5798             date.roll!"months"(-14, AllowDayOverflow.no);
5799             assert(date == Date(-1997, 12, 28));
5800         }
5801 
5802         {
5803             auto date = Date(-2002, 12, 31);
5804             date.roll!"months"(14, AllowDayOverflow.no);
5805             assert(date == Date(-2002, 2, 28));
5806             date.roll!"months"(-14, AllowDayOverflow.no);
5807             assert(date == Date(-2002, 12, 28));
5808         }
5809 
5810         {
5811             auto date = Date(-2001, 12, 31);
5812             date.roll!"months"(14, AllowDayOverflow.no);
5813             assert(date == Date(-2001, 2, 28));
5814             date.roll!"months"(-14, AllowDayOverflow.no);
5815             assert(date == Date(-2001, 12, 28));
5816         }
5817 
5818         // Test Both
5819         {
5820             auto date = Date(1, 1, 1);
5821             date.roll!"months"(-1, AllowDayOverflow.no);
5822             assert(date == Date(1, 12, 1));
5823             date.roll!"months"(1, AllowDayOverflow.no);
5824             assert(date == Date(1, 1, 1));
5825         }
5826 
5827         {
5828             auto date = Date(4, 1, 1);
5829             date.roll!"months"(-48, AllowDayOverflow.no);
5830             assert(date == Date(4, 1, 1));
5831             date.roll!"months"(48, AllowDayOverflow.no);
5832             assert(date == Date(4, 1, 1));
5833         }
5834 
5835         {
5836             auto date = Date(4, 3, 31);
5837             date.roll!"months"(-49, AllowDayOverflow.no);
5838             assert(date == Date(4, 2, 29));
5839             date.roll!"months"(49, AllowDayOverflow.no);
5840             assert(date == Date(4, 3, 29));
5841         }
5842 
5843         {
5844             auto date = Date(4, 3, 31);
5845             date.roll!"months"(-85, AllowDayOverflow.no);
5846             assert(date == Date(4, 2, 29));
5847             date.roll!"months"(85, AllowDayOverflow.no);
5848             assert(date == Date(4, 3, 29));
5849         }
5850 
5851         {
5852             auto date = Date(-1, 1, 1);
5853             date.roll!"months"(-1, AllowDayOverflow.no);
5854             assert(date == Date(-1, 12, 1));
5855             date.roll!"months"(1, AllowDayOverflow.no);
5856             assert(date == Date(-1, 1, 1));
5857         }
5858 
5859         {
5860             auto date = Date(-4, 1, 1);
5861             date.roll!"months"(-48, AllowDayOverflow.no);
5862             assert(date == Date(-4, 1, 1));
5863             date.roll!"months"(48, AllowDayOverflow.no);
5864             assert(date == Date(-4, 1, 1));
5865         }
5866 
5867         {
5868             auto date = Date(-4, 3, 31);
5869             date.roll!"months"(-49, AllowDayOverflow.no);
5870             assert(date == Date(-4, 2, 29));
5871             date.roll!"months"(49, AllowDayOverflow.no);
5872             assert(date == Date(-4, 3, 29));
5873         }
5874 
5875         {
5876             auto date = Date(-4, 3, 31);
5877             date.roll!"months"(-85, AllowDayOverflow.no);
5878             assert(date == Date(-4, 2, 29));
5879             date.roll!"months"(85, AllowDayOverflow.no);
5880             assert(date == Date(-4, 3, 29));
5881         }
5882 
5883         {
5884             auto date = Date(-3, 3, 31);
5885             date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
5886             assert(date == Date(-3, 5, 30));
5887         }
5888     }
5889 
5890 
5891     /++
5892         Adds the given number of units to this $(LREF Date), mutating it. A
5893         negative number will subtract.
5894 
5895         The difference between rolling and adding is that rolling does not
5896         affect larger units. For instance, rolling a $(LREF Date) one
5897         year's worth of days gets the exact same $(LREF Date).
5898 
5899         The only accepted units are `"days"`.
5900 
5901         Params:
5902             units = The units to add. Must be `"days"`.
5903             days  = The number of days to add to this $(LREF Date).
5904 
5905         Returns:
5906             A reference to the `Date` (`this`).
5907       +/
5908     ref Date roll(string units)(long days) @safe pure nothrow @nogc
5909         if (units == "days")
5910     {
5911         immutable limit = maxDay(_year, _month);
5912         days %= limit;
5913         auto newDay = _day + days;
5914 
5915         if (days < 0)
5916         {
5917             if (newDay < 1)
5918                 newDay += limit;
5919         }
5920         else if (newDay > limit)
5921             newDay -= limit;
5922 
5923         _day = cast(ubyte) newDay;
5924         return this;
5925     }
5926 
5927     ///
5928     @safe unittest
5929     {
5930         auto d = Date(2010, 1, 1);
5931         d.roll!"days"(1);
5932         assert(d == Date(2010, 1, 2));
5933         d.roll!"days"(365);
5934         assert(d == Date(2010, 1, 26));
5935         d.roll!"days"(-32);
5936         assert(d == Date(2010, 1, 25));
5937     }
5938 
5939     @safe unittest
5940     {
5941         // Test A.D.
5942         {
5943             auto date = Date(1999, 2, 28);
5944             date.roll!"days"(1);
5945             assert(date == Date(1999, 2, 1));
5946             date.roll!"days"(-1);
5947             assert(date == Date(1999, 2, 28));
5948         }
5949 
5950         {
5951             auto date = Date(2000, 2, 28);
5952             date.roll!"days"(1);
5953             assert(date == Date(2000, 2, 29));
5954             date.roll!"days"(1);
5955             assert(date == Date(2000, 2, 1));
5956             date.roll!"days"(-1);
5957             assert(date == Date(2000, 2, 29));
5958         }
5959 
5960         {
5961             auto date = Date(1999, 6, 30);
5962             date.roll!"days"(1);
5963             assert(date == Date(1999, 6, 1));
5964             date.roll!"days"(-1);
5965             assert(date == Date(1999, 6, 30));
5966         }
5967 
5968         {
5969             auto date = Date(1999, 7, 31);
5970             date.roll!"days"(1);
5971             assert(date == Date(1999, 7, 1));
5972             date.roll!"days"(-1);
5973             assert(date == Date(1999, 7, 31));
5974         }
5975 
5976         {
5977             auto date = Date(1999, 1, 1);
5978             date.roll!"days"(-1);
5979             assert(date == Date(1999, 1, 31));
5980             date.roll!"days"(1);
5981             assert(date == Date(1999, 1, 1));
5982         }
5983 
5984         {
5985             auto date = Date(1999, 7, 6);
5986             date.roll!"days"(9);
5987             assert(date == Date(1999, 7, 15));
5988             date.roll!"days"(-11);
5989             assert(date == Date(1999, 7, 4));
5990             date.roll!"days"(30);
5991             assert(date == Date(1999, 7, 3));
5992             date.roll!"days"(-3);
5993             assert(date == Date(1999, 7, 31));
5994         }
5995 
5996         {
5997             auto date = Date(1999, 7, 6);
5998             date.roll!"days"(365);
5999             assert(date == Date(1999, 7, 30));
6000             date.roll!"days"(-365);
6001             assert(date == Date(1999, 7, 6));
6002             date.roll!"days"(366);
6003             assert(date == Date(1999, 7, 31));
6004             date.roll!"days"(730);
6005             assert(date == Date(1999, 7, 17));
6006             date.roll!"days"(-1096);
6007             assert(date == Date(1999, 7, 6));
6008         }
6009 
6010         {
6011             auto date = Date(1999, 2, 6);
6012             date.roll!"days"(365);
6013             assert(date == Date(1999, 2, 7));
6014             date.roll!"days"(-365);
6015             assert(date == Date(1999, 2, 6));
6016             date.roll!"days"(366);
6017             assert(date == Date(1999, 2, 8));
6018             date.roll!"days"(730);
6019             assert(date == Date(1999, 2, 10));
6020             date.roll!"days"(-1096);
6021             assert(date == Date(1999, 2, 6));
6022         }
6023 
6024         // Test B.C.
6025         {
6026             auto date = Date(-1999, 2, 28);
6027             date.roll!"days"(1);
6028             assert(date == Date(-1999, 2, 1));
6029             date.roll!"days"(-1);
6030             assert(date == Date(-1999, 2, 28));
6031         }
6032 
6033         {
6034             auto date = Date(-2000, 2, 28);
6035             date.roll!"days"(1);
6036             assert(date == Date(-2000, 2, 29));
6037             date.roll!"days"(1);
6038             assert(date == Date(-2000, 2, 1));
6039             date.roll!"days"(-1);
6040             assert(date == Date(-2000, 2, 29));
6041         }
6042 
6043         {
6044             auto date = Date(-1999, 6, 30);
6045             date.roll!"days"(1);
6046             assert(date == Date(-1999, 6, 1));
6047             date.roll!"days"(-1);
6048             assert(date == Date(-1999, 6, 30));
6049         }
6050 
6051         {
6052             auto date = Date(-1999, 7, 31);
6053             date.roll!"days"(1);
6054             assert(date == Date(-1999, 7, 1));
6055             date.roll!"days"(-1);
6056             assert(date == Date(-1999, 7, 31));
6057         }
6058 
6059         {
6060             auto date = Date(-1999, 1, 1);
6061             date.roll!"days"(-1);
6062             assert(date == Date(-1999, 1, 31));
6063             date.roll!"days"(1);
6064             assert(date == Date(-1999, 1, 1));
6065         }
6066 
6067         {
6068             auto date = Date(-1999, 7, 6);
6069             date.roll!"days"(9);
6070             assert(date == Date(-1999, 7, 15));
6071             date.roll!"days"(-11);
6072             assert(date == Date(-1999, 7, 4));
6073             date.roll!"days"(30);
6074             assert(date == Date(-1999, 7, 3));
6075             date.roll!"days"(-3);
6076             assert(date == Date(-1999, 7, 31));
6077         }
6078 
6079         {
6080             auto date = Date(-1999, 7, 6);
6081             date.roll!"days"(365);
6082             assert(date == Date(-1999, 7, 30));
6083             date.roll!"days"(-365);
6084             assert(date == Date(-1999, 7, 6));
6085             date.roll!"days"(366);
6086             assert(date == Date(-1999, 7, 31));
6087             date.roll!"days"(730);
6088             assert(date == Date(-1999, 7, 17));
6089             date.roll!"days"(-1096);
6090             assert(date == Date(-1999, 7, 6));
6091         }
6092 
6093         // Test Both
6094         {
6095             auto date = Date(1, 7, 6);
6096             date.roll!"days"(-365);
6097             assert(date == Date(1, 7, 13));
6098             date.roll!"days"(365);
6099             assert(date == Date(1, 7, 6));
6100             date.roll!"days"(-731);
6101             assert(date == Date(1, 7, 19));
6102             date.roll!"days"(730);
6103             assert(date == Date(1, 7, 5));
6104         }
6105 
6106         {
6107             auto date = Date(0, 7, 6);
6108             date.roll!"days"(-365);
6109             assert(date == Date(0, 7, 13));
6110             date.roll!"days"(365);
6111             assert(date == Date(0, 7, 6));
6112             date.roll!"days"(-731);
6113             assert(date == Date(0, 7, 19));
6114             date.roll!"days"(730);
6115             assert(date == Date(0, 7, 5));
6116         }
6117 
6118         {
6119             auto date = Date(0, 7, 6);
6120             date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
6121             assert(date == Date(0, 7, 8));
6122         }
6123 
6124         const cdate = Date(1999, 7, 6);
6125         immutable idate = Date(1999, 7, 6);
6126         static assert(!__traits(compiles, cdate.roll!"days"(12)));
6127         static assert(!__traits(compiles, idate.roll!"days"(12)));
6128     }
6129 
6130     import core.time : Duration;
6131     /++
6132         Gives the result of adding or subtracting a $(REF Duration, core,time)
6133         from
6134 
6135         The legal types of arithmetic for $(LREF Date) using this operator are
6136 
6137         $(BOOKTABLE,
6138         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6139         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6140         )
6141 
6142         Params:
6143             duration = The $(REF Duration, core,time) to add to or subtract from
6144                        this $(LREF Date).
6145       +/
6146     Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
6147         if (op == "+" || op == "-")
6148     {
6149         Date retval = this;
6150         immutable days = duration.total!"days";
6151         mixin("return retval._addDays(" ~ op ~ "days);");
6152     }
6153 
6154     ///
6155     @safe unittest
6156     {
6157         import core.time : days;
6158 
6159         assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
6160         assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
6161 
6162         assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
6163         assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
6164     }
6165 
6166     @safe unittest
6167     {
6168         auto date = Date(1999, 7, 6);
6169 
6170         import core.time : dur;
6171         assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
6172         assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
6173         assert(date + dur!"days"(7) == Date(1999, 7, 13));
6174         assert(date + dur!"days"(-7) == Date(1999, 6, 29));
6175 
6176         assert(date + dur!"hours"(24) == Date(1999, 7, 7));
6177         assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
6178         assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
6179         assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
6180         assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
6181         assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6182         assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6183         assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6184         assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6185         assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6186         assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6187         assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6188 
6189         assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
6190         assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
6191         assert(date - dur!"days"(-7) == Date(1999, 7, 13));
6192         assert(date - dur!"days"(7) == Date(1999, 6, 29));
6193 
6194         assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
6195         assert(date - dur!"hours"(24) == Date(1999, 7, 5));
6196         assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
6197         assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
6198         assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6199         assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
6200         assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6201         assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6202         assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6203         assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6204         assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6205         assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6206 
6207         auto duration = dur!"days"(12);
6208         const cdate = Date(1999, 7, 6);
6209         immutable idate = Date(1999, 7, 6);
6210         assert(date + duration == Date(1999, 7, 18));
6211         assert(cdate + duration == Date(1999, 7, 18));
6212         assert(idate + duration == Date(1999, 7, 18));
6213 
6214         assert(date - duration == Date(1999, 6, 24));
6215         assert(cdate - duration == Date(1999, 6, 24));
6216         assert(idate - duration == Date(1999, 6, 24));
6217     }
6218 
6219 
6220     /++
6221         Gives the result of adding or subtracting a $(REF Duration, core,time)
6222         from this $(LREF Date), as well as assigning the result to this
6223         $(LREF Date).
6224 
6225         The legal types of arithmetic for $(LREF Date) using this operator are
6226 
6227         $(BOOKTABLE,
6228         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6229         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6230         )
6231 
6232         Params:
6233             duration = The $(REF Duration, core,time) to add to or subtract from
6234                        this $(LREF Date).
6235       +/
6236     ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
6237         if (op == "+" || op == "-")
6238     {
6239         immutable days = duration.total!"days";
6240         mixin("return _addDays(" ~ op ~ "days);");
6241     }
6242 
6243     @safe unittest
6244     {
6245         import core.time : dur;
6246         assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
6247         assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
6248         assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
6249         assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
6250 
6251         assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
6252         assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
6253         assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
6254         assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
6255         assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
6256         assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6257         assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6258         assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6259         assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6260         assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6261         assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6262         assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6263 
6264         assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
6265         assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
6266         assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
6267         assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
6268 
6269         assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
6270         assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
6271         assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
6272         assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
6273         assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6274         assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
6275         assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6276         assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6277         assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6278         assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6279         assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6280         assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6281 
6282         {
6283             auto date = Date(0, 1, 31);
6284             (date += dur!"days"(507)) += dur!"days"(-2);
6285             assert(date == Date(1, 6, 19));
6286         }
6287 
6288         auto duration = dur!"days"(12);
6289         auto date = Date(1999, 7, 6);
6290         const cdate = Date(1999, 7, 6);
6291         immutable idate = Date(1999, 7, 6);
6292         date += duration;
6293         static assert(!__traits(compiles, cdate += duration));
6294         static assert(!__traits(compiles, idate += duration));
6295 
6296         date -= duration;
6297         static assert(!__traits(compiles, cdate -= duration));
6298         static assert(!__traits(compiles, idate -= duration));
6299     }
6300 
6301 
6302     /++
6303         Gives the difference between two $(LREF Date)s.
6304 
6305         The legal types of arithmetic for $(LREF Date) using this operator are
6306 
6307         $(BOOKTABLE,
6308         $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
6309         )
6310       +/
6311     Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
6312         if (op == "-")
6313     {
6314         import core.time : dur;
6315         return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
6316     }
6317 
6318     @safe unittest
6319     {
6320         auto date = Date(1999, 7, 6);
6321 
6322         import core.time : dur;
6323         assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
6324         assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
6325         assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
6326         assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
6327         assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
6328         assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
6329 
6330         const cdate = Date(1999, 7, 6);
6331         immutable idate = Date(1999, 7, 6);
6332         assert(date - date == Duration.zero);
6333         assert(cdate - date == Duration.zero);
6334         assert(idate - date == Duration.zero);
6335 
6336         assert(date - cdate == Duration.zero);
6337         assert(cdate - cdate == Duration.zero);
6338         assert(idate - cdate == Duration.zero);
6339 
6340         assert(date - idate == Duration.zero);
6341         assert(cdate - idate == Duration.zero);
6342         assert(idate - idate == Duration.zero);
6343     }
6344 
6345 
6346     /++
6347         Returns the difference between the two $(LREF Date)s in months.
6348 
6349         To get the difference in years, subtract the year property
6350         of two $(LREF Date)s. To get the difference in days or weeks,
6351         subtract the $(LREF Date)s themselves and use the
6352         $(REF Duration, core,time) that results. Because converting between
6353         months and smaller units requires a specific date (which
6354         $(REF Duration, core,time)s don't have), getting the difference in
6355         months requires some math using both the year and month properties, so
6356         this is a convenience function for getting the difference in months.
6357 
6358         Note that the number of days in the months or how far into the month
6359         either $(LREF Date) is is irrelevant. It is the difference in the month
6360         property combined with the difference in years * 12. So, for instance,
6361         December 31st and January 1st are one month apart just as December 1st
6362         and January 31st are one month apart.
6363 
6364         Params:
6365             rhs = The $(LREF Date) to subtract from this one.
6366       +/
6367     int diffMonths(Date rhs) const @safe pure nothrow @nogc
6368     {
6369         immutable yearDiff = _year - rhs._year;
6370         immutable monthDiff = _month - rhs._month;
6371 
6372         return yearDiff * 12 + monthDiff;
6373     }
6374 
6375     ///
6376     @safe unittest
6377     {
6378         assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
6379         assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
6380         assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
6381         assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
6382     }
6383 
6384     @safe unittest
6385     {
6386         auto date = Date(1999, 7, 6);
6387 
6388         // Test A.D.
6389         assert(date.diffMonths(Date(1998, 6, 5)) == 13);
6390         assert(date.diffMonths(Date(1998, 7, 5)) == 12);
6391         assert(date.diffMonths(Date(1998, 8, 5)) == 11);
6392         assert(date.diffMonths(Date(1998, 9, 5)) == 10);
6393         assert(date.diffMonths(Date(1998, 10, 5)) == 9);
6394         assert(date.diffMonths(Date(1998, 11, 5)) == 8);
6395         assert(date.diffMonths(Date(1998, 12, 5)) == 7);
6396         assert(date.diffMonths(Date(1999, 1, 5)) == 6);
6397         assert(date.diffMonths(Date(1999, 2, 6)) == 5);
6398         assert(date.diffMonths(Date(1999, 3, 6)) == 4);
6399         assert(date.diffMonths(Date(1999, 4, 6)) == 3);
6400         assert(date.diffMonths(Date(1999, 5, 6)) == 2);
6401         assert(date.diffMonths(Date(1999, 6, 6)) == 1);
6402         assert(date.diffMonths(date) == 0);
6403         assert(date.diffMonths(Date(1999, 8, 6)) == -1);
6404         assert(date.diffMonths(Date(1999, 9, 6)) == -2);
6405         assert(date.diffMonths(Date(1999, 10, 6)) == -3);
6406         assert(date.diffMonths(Date(1999, 11, 6)) == -4);
6407         assert(date.diffMonths(Date(1999, 12, 6)) == -5);
6408         assert(date.diffMonths(Date(2000, 1, 6)) == -6);
6409         assert(date.diffMonths(Date(2000, 2, 6)) == -7);
6410         assert(date.diffMonths(Date(2000, 3, 6)) == -8);
6411         assert(date.diffMonths(Date(2000, 4, 6)) == -9);
6412         assert(date.diffMonths(Date(2000, 5, 6)) == -10);
6413         assert(date.diffMonths(Date(2000, 6, 6)) == -11);
6414         assert(date.diffMonths(Date(2000, 7, 6)) == -12);
6415         assert(date.diffMonths(Date(2000, 8, 6)) == -13);
6416 
6417         assert(Date(1998, 6, 5).diffMonths(date) == -13);
6418         assert(Date(1998, 7, 5).diffMonths(date) == -12);
6419         assert(Date(1998, 8, 5).diffMonths(date) == -11);
6420         assert(Date(1998, 9, 5).diffMonths(date) == -10);
6421         assert(Date(1998, 10, 5).diffMonths(date) == -9);
6422         assert(Date(1998, 11, 5).diffMonths(date) == -8);
6423         assert(Date(1998, 12, 5).diffMonths(date) == -7);
6424         assert(Date(1999, 1, 5).diffMonths(date) == -6);
6425         assert(Date(1999, 2, 6).diffMonths(date) == -5);
6426         assert(Date(1999, 3, 6).diffMonths(date) == -4);
6427         assert(Date(1999, 4, 6).diffMonths(date) == -3);
6428         assert(Date(1999, 5, 6).diffMonths(date) == -2);
6429         assert(Date(1999, 6, 6).diffMonths(date) == -1);
6430         assert(Date(1999, 8, 6).diffMonths(date) == 1);
6431         assert(Date(1999, 9, 6).diffMonths(date) == 2);
6432         assert(Date(1999, 10, 6).diffMonths(date) == 3);
6433         assert(Date(1999, 11, 6).diffMonths(date) == 4);
6434         assert(Date(1999, 12, 6).diffMonths(date) == 5);
6435         assert(Date(2000, 1, 6).diffMonths(date) == 6);
6436         assert(Date(2000, 2, 6).diffMonths(date) == 7);
6437         assert(Date(2000, 3, 6).diffMonths(date) == 8);
6438         assert(Date(2000, 4, 6).diffMonths(date) == 9);
6439         assert(Date(2000, 5, 6).diffMonths(date) == 10);
6440         assert(Date(2000, 6, 6).diffMonths(date) == 11);
6441         assert(Date(2000, 7, 6).diffMonths(date) == 12);
6442         assert(Date(2000, 8, 6).diffMonths(date) == 13);
6443 
6444         assert(date.diffMonths(Date(1999, 6, 30)) == 1);
6445         assert(date.diffMonths(Date(1999, 7, 1)) == 0);
6446         assert(date.diffMonths(Date(1999, 7, 6)) == 0);
6447         assert(date.diffMonths(Date(1999, 7, 11)) == 0);
6448         assert(date.diffMonths(Date(1999, 7, 16)) == 0);
6449         assert(date.diffMonths(Date(1999, 7, 21)) == 0);
6450         assert(date.diffMonths(Date(1999, 7, 26)) == 0);
6451         assert(date.diffMonths(Date(1999, 7, 31)) == 0);
6452         assert(date.diffMonths(Date(1999, 8, 1)) == -1);
6453 
6454         assert(date.diffMonths(Date(1990, 6, 30)) == 109);
6455         assert(date.diffMonths(Date(1990, 7, 1)) == 108);
6456         assert(date.diffMonths(Date(1990, 7, 6)) == 108);
6457         assert(date.diffMonths(Date(1990, 7, 11)) == 108);
6458         assert(date.diffMonths(Date(1990, 7, 16)) == 108);
6459         assert(date.diffMonths(Date(1990, 7, 21)) == 108);
6460         assert(date.diffMonths(Date(1990, 7, 26)) == 108);
6461         assert(date.diffMonths(Date(1990, 7, 31)) == 108);
6462         assert(date.diffMonths(Date(1990, 8, 1)) == 107);
6463 
6464         assert(Date(1999, 6, 30).diffMonths(date) == -1);
6465         assert(Date(1999, 7, 1).diffMonths(date) == 0);
6466         assert(Date(1999, 7, 6).diffMonths(date) == 0);
6467         assert(Date(1999, 7, 11).diffMonths(date) == 0);
6468         assert(Date(1999, 7, 16).diffMonths(date) == 0);
6469         assert(Date(1999, 7, 21).diffMonths(date) == 0);
6470         assert(Date(1999, 7, 26).diffMonths(date) == 0);
6471         assert(Date(1999, 7, 31).diffMonths(date) == 0);
6472         assert(Date(1999, 8, 1).diffMonths(date) == 1);
6473 
6474         assert(Date(1990, 6, 30).diffMonths(date) == -109);
6475         assert(Date(1990, 7, 1).diffMonths(date) == -108);
6476         assert(Date(1990, 7, 6).diffMonths(date) == -108);
6477         assert(Date(1990, 7, 11).diffMonths(date) == -108);
6478         assert(Date(1990, 7, 16).diffMonths(date) == -108);
6479         assert(Date(1990, 7, 21).diffMonths(date) == -108);
6480         assert(Date(1990, 7, 26).diffMonths(date) == -108);
6481         assert(Date(1990, 7, 31).diffMonths(date) == -108);
6482         assert(Date(1990, 8, 1).diffMonths(date) == -107);
6483 
6484         // Test B.C.
6485         auto dateBC = Date(-1999, 7, 6);
6486 
6487         assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
6488         assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
6489         assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
6490         assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
6491         assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
6492         assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
6493         assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
6494         assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
6495         assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
6496         assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
6497         assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
6498         assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
6499         assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
6500         assert(dateBC.diffMonths(dateBC) == 0);
6501         assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
6502         assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
6503         assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
6504         assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
6505         assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
6506         assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
6507         assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
6508         assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
6509         assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
6510         assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
6511         assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
6512         assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
6513         assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
6514 
6515         assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
6516         assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
6517         assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
6518         assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
6519         assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
6520         assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
6521         assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
6522         assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
6523         assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
6524         assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
6525         assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
6526         assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
6527         assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
6528         assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
6529         assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
6530         assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
6531         assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
6532         assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
6533         assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
6534         assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
6535         assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
6536         assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
6537         assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
6538         assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
6539         assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
6540         assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
6541 
6542         assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
6543         assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
6544         assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
6545         assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
6546         assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
6547         assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
6548         assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
6549         assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
6550         assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
6551 
6552         assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
6553         assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
6554         assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
6555         assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
6556         assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
6557         assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
6558         assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
6559         assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
6560         assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
6561 
6562         assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
6563         assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
6564         assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
6565         assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
6566         assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
6567         assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
6568         assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
6569         assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
6570         assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
6571 
6572         assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
6573         assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
6574         assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
6575         assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
6576         assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
6577         assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
6578         assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
6579         assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
6580         assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
6581 
6582         // Test Both
6583         assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
6584         assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
6585 
6586         const cdate = Date(1999, 7, 6);
6587         immutable idate = Date(1999, 7, 6);
6588         assert(date.diffMonths(date) == 0);
6589         assert(cdate.diffMonths(date) == 0);
6590         assert(idate.diffMonths(date) == 0);
6591 
6592         assert(date.diffMonths(cdate) == 0);
6593         assert(cdate.diffMonths(cdate) == 0);
6594         assert(idate.diffMonths(cdate) == 0);
6595 
6596         assert(date.diffMonths(idate) == 0);
6597         assert(cdate.diffMonths(idate) == 0);
6598         assert(idate.diffMonths(idate) == 0);
6599     }
6600 
6601 
6602     /++
6603         Whether this $(LREF Date) is in a leap year.
6604      +/
6605     @property bool isLeapYear() const @safe pure nothrow @nogc
6606     {
6607         return yearIsLeapYear(_year);
6608     }
6609 
6610     @safe unittest
6611     {
6612         auto date = Date(1999, 7, 6);
6613         const cdate = Date(1999, 7, 6);
6614         immutable idate = Date(1999, 7, 6);
6615         static assert(!__traits(compiles, date.isLeapYear = true));
6616         static assert(!__traits(compiles, cdate.isLeapYear = true));
6617         static assert(!__traits(compiles, idate.isLeapYear = true));
6618     }
6619 
6620 
6621     /++
6622         Day of the week this $(LREF Date) is on.
6623       +/
6624     @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
6625     {
6626         return getDayOfWeek(dayOfGregorianCal);
6627     }
6628 
6629     @safe unittest
6630     {
6631         const cdate = Date(1999, 7, 6);
6632         immutable idate = Date(1999, 7, 6);
6633         assert(cdate.dayOfWeek == DayOfWeek.tue);
6634         static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
6635         assert(idate.dayOfWeek == DayOfWeek.tue);
6636         static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
6637     }
6638 
6639 
6640     /++
6641         Day of the year this $(LREF Date) is on.
6642       +/
6643     @property ushort dayOfYear() const @safe pure nothrow @nogc
6644     {
6645         if (_month >= Month.jan && _month <= Month.dec)
6646         {
6647             immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6648             auto monthIndex = _month - Month.jan;
6649 
6650             return cast(ushort)(lastDay[monthIndex] + _day);
6651         }
6652         assert(0, "Invalid month.");
6653     }
6654 
6655     ///
6656     @safe unittest
6657     {
6658         assert(Date(1999, 1, 1).dayOfYear == 1);
6659         assert(Date(1999, 12, 31).dayOfYear == 365);
6660         assert(Date(2000, 12, 31).dayOfYear == 366);
6661     }
6662 
6663     @safe unittest
6664     {
6665         import std.algorithm.iteration : filter;
6666         import std.range : chain;
6667 
6668         foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6669         {
6670             foreach (doy; testDaysOfYear)
6671                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6672         }
6673 
6674         foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6675         {
6676             foreach (doy; testDaysOfLeapYear)
6677                 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6678         }
6679 
6680         const cdate = Date(1999, 7, 6);
6681         immutable idate = Date(1999, 7, 6);
6682         assert(cdate.dayOfYear == 187);
6683         assert(idate.dayOfYear == 187);
6684     }
6685 
6686     /++
6687         Day of the year.
6688 
6689         Params:
6690             day = The day of the year to set which day of the year this
6691                   $(LREF Date) is on.
6692 
6693         Throws:
6694             $(REF DateTimeException,std,datetime,date) if the given day is an
6695             invalid day of the year.
6696       +/
6697     @property void dayOfYear(int day) @safe pure
6698     {
6699         setDayOfYear!true(day);
6700     }
6701 
6702     private void setDayOfYear(bool useExceptions = false)(int day)
6703     {
6704         immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6705 
6706         bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
6707         enum errorMsg = "Invalid day of the year.";
6708 
6709         static if (useExceptions)
6710         {
6711             if (dayOutOfRange) throw new DateTimeException(errorMsg);
6712         }
6713         else
6714         {
6715             assert(!dayOutOfRange, errorMsg);
6716         }
6717 
6718         foreach (i; 1 .. lastDay.length)
6719         {
6720             if (day <= lastDay[i])
6721             {
6722                 _month = cast(Month)(cast(int) Month.jan + i - 1);
6723                 _day = cast(ubyte)(day - lastDay[i - 1]);
6724                 return;
6725             }
6726         }
6727         assert(0, "Invalid day of the year.");
6728     }
6729 
6730     @safe unittest
6731     {
6732         static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
6733         {
6734             date.dayOfYear = day;
6735             assert(date.month == expected.month);
6736             assert(date.day == expected.day);
6737         }
6738 
6739         foreach (doy; testDaysOfYear)
6740         {
6741             test(Date(1999, 1, 1), doy.day, doy.md);
6742             test(Date(-1, 1, 1), doy.day, doy.md);
6743         }
6744 
6745         foreach (doy; testDaysOfLeapYear)
6746         {
6747             test(Date(2000, 1, 1), doy.day, doy.md);
6748             test(Date(-4, 1, 1), doy.day, doy.md);
6749         }
6750 
6751         const cdate = Date(1999, 7, 6);
6752         immutable idate = Date(1999, 7, 6);
6753         static assert(!__traits(compiles, cdate.dayOfYear = 187));
6754         static assert(!__traits(compiles, idate.dayOfYear = 187));
6755     }
6756 
6757 
6758     /++
6759         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6760      +/
6761     @property int dayOfGregorianCal() const @safe pure nothrow @nogc
6762     {
6763         if (isAD)
6764         {
6765             if (_year == 1)
6766                 return dayOfYear;
6767 
6768             int years = _year - 1;
6769             auto days = (years / 400) * daysIn400Years;
6770             years %= 400;
6771 
6772             days += (years / 100) * daysIn100Years;
6773             years %= 100;
6774 
6775             days += (years / 4) * daysIn4Years;
6776             years %= 4;
6777 
6778             days += years * daysInYear;
6779 
6780             days += dayOfYear;
6781 
6782             return days;
6783         }
6784         else if (_year == 0)
6785             return dayOfYear - daysInLeapYear;
6786         else
6787         {
6788             int years = _year;
6789             auto days = (years / 400) * daysIn400Years;
6790             years %= 400;
6791 
6792             days += (years / 100) * daysIn100Years;
6793             years %= 100;
6794 
6795             days += (years / 4) * daysIn4Years;
6796             years %= 4;
6797 
6798             if (years < 0)
6799             {
6800                 days -= daysInLeapYear;
6801                 ++years;
6802 
6803                 days += years * daysInYear;
6804 
6805                 days -= daysInYear - dayOfYear;
6806             }
6807             else
6808                 days -= daysInLeapYear - dayOfYear;
6809 
6810             return days;
6811         }
6812     }
6813 
6814     ///
6815     @safe unittest
6816     {
6817         assert(Date(1, 1, 1).dayOfGregorianCal == 1);
6818         assert(Date(1, 12, 31).dayOfGregorianCal == 365);
6819         assert(Date(2, 1, 1).dayOfGregorianCal == 366);
6820 
6821         assert(Date(0, 12, 31).dayOfGregorianCal == 0);
6822         assert(Date(0, 1, 1).dayOfGregorianCal == -365);
6823         assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
6824 
6825         assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
6826         assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
6827     }
6828 
6829     @safe unittest
6830     {
6831         import std.range : chain;
6832 
6833         foreach (gd; chain(testGregDaysBC, testGregDaysAD))
6834             assert(gd.date.dayOfGregorianCal == gd.day);
6835 
6836         auto date = Date(1999, 7, 6);
6837         const cdate = Date(1999, 7, 6);
6838         immutable idate = Date(1999, 7, 6);
6839         assert(date.dayOfGregorianCal == 729_941);
6840         assert(cdate.dayOfGregorianCal == 729_941);
6841         assert(idate.dayOfGregorianCal == 729_941);
6842     }
6843 
6844     /++
6845         The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6846 
6847         Params:
6848             day = The day of the Gregorian Calendar to set this $(LREF Date) to.
6849      +/
6850     @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
6851     {
6852         this = Date(day);
6853     }
6854 
6855     ///
6856     @safe unittest
6857     {
6858         auto date = Date.init;
6859         date.dayOfGregorianCal = 1;
6860         assert(date == Date(1, 1, 1));
6861 
6862         date.dayOfGregorianCal = 365;
6863         assert(date == Date(1, 12, 31));
6864 
6865         date.dayOfGregorianCal = 366;
6866         assert(date == Date(2, 1, 1));
6867 
6868         date.dayOfGregorianCal = 0;
6869         assert(date == Date(0, 12, 31));
6870 
6871         date.dayOfGregorianCal = -365;
6872         assert(date == Date(-0, 1, 1));
6873 
6874         date.dayOfGregorianCal = -366;
6875         assert(date == Date(-1, 12, 31));
6876 
6877         date.dayOfGregorianCal = 730_120;
6878         assert(date == Date(2000, 1, 1));
6879 
6880         date.dayOfGregorianCal = 734_137;
6881         assert(date == Date(2010, 12, 31));
6882     }
6883 
6884     @safe unittest
6885     {
6886         auto date = Date(1999, 7, 6);
6887         const cdate = Date(1999, 7, 6);
6888         immutable idate = Date(1999, 7, 6);
6889         date.dayOfGregorianCal = 187;
6890         assert(date.dayOfGregorianCal == 187);
6891         static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
6892         static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
6893     }
6894 
6895 
6896     /++
6897         The ISO 8601 week and year of the year that this $(LREF Date) is in.
6898 
6899         Returns:
6900             An anonymous struct with the members $(D isoWeekYear) for the
6901             resulting year and $(D isoWeek) for the resulting ISO week.
6902 
6903         See_Also:
6904             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6905       +/
6906     @property auto isoWeekAndYear() const @safe pure nothrow
6907     {
6908         struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
6909 
6910         immutable weekday = dayOfWeek;
6911         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
6912         immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
6913 
6914         try
6915         {
6916             if (week == 53)
6917             {
6918                 switch (Date(_year + 1, 1, 1).dayOfWeek)
6919                 {
6920                     case DayOfWeek.mon:
6921                     case DayOfWeek.tue:
6922                     case DayOfWeek.wed:
6923                     case DayOfWeek.thu:
6924                         return ISOWeekAndYear(cast(short) (_year + 1), 1);
6925                     case DayOfWeek.fri:
6926                     case DayOfWeek.sat:
6927                     case DayOfWeek.sun:
6928                         return ISOWeekAndYear(_year, 53);
6929                     default:
6930                         assert(0, "Invalid ISO Week");
6931                 }
6932             }
6933             else if (week > 0)
6934                 return ISOWeekAndYear(_year, cast(ubyte) week);
6935             else
6936                 return Date(_year - 1, 12, 31).isoWeekAndYear;
6937         }
6938         catch (Exception e)
6939             assert(0, "Date's constructor threw.");
6940     }
6941 
6942     /++
6943         The ISO 8601 week of the year that this $(LREF Date) is in.
6944 
6945         See_Also:
6946             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6947       +/
6948     @property ubyte isoWeek() const @safe pure nothrow
6949     {
6950         return isoWeekAndYear().isoWeek;
6951     }
6952 
6953     @safe unittest
6954     {
6955         // Test A.D.
6956         assert(Date(2009, 12, 28).isoWeek == 53);
6957         assert(Date(2009, 12, 29).isoWeek == 53);
6958         assert(Date(2009, 12, 30).isoWeek == 53);
6959         assert(Date(2009, 12, 31).isoWeek == 53);
6960         assert(Date(2010, 1, 1).isoWeek == 53);
6961         assert(Date(2010, 1, 2).isoWeek == 53);
6962         assert(Date(2010, 1, 3).isoWeek == 53);
6963         assert(Date(2010, 1, 4).isoWeek == 1);
6964         assert(Date(2010, 1, 5).isoWeek == 1);
6965         assert(Date(2010, 1, 6).isoWeek == 1);
6966         assert(Date(2010, 1, 7).isoWeek == 1);
6967         assert(Date(2010, 1, 8).isoWeek == 1);
6968         assert(Date(2010, 1, 9).isoWeek == 1);
6969         assert(Date(2010, 1, 10).isoWeek == 1);
6970         assert(Date(2010, 1, 11).isoWeek == 2);
6971         assert(Date(2010, 12, 31).isoWeek == 52);
6972 
6973         assert(Date(2004, 12, 26).isoWeek == 52);
6974         assert(Date(2004, 12, 27).isoWeek == 53);
6975         assert(Date(2004, 12, 28).isoWeek == 53);
6976         assert(Date(2004, 12, 29).isoWeek == 53);
6977         assert(Date(2004, 12, 30).isoWeek == 53);
6978         assert(Date(2004, 12, 31).isoWeek == 53);
6979         assert(Date(2005, 1, 1).isoWeek == 53);
6980         assert(Date(2005, 1, 2).isoWeek == 53);
6981 
6982         assert(Date(2005, 12, 31).isoWeek == 52);
6983         assert(Date(2007, 1, 1).isoWeek == 1);
6984 
6985         assert(Date(2007, 12, 30).isoWeek == 52);
6986         assert(Date(2007, 12, 31).isoWeek == 1);
6987         assert(Date(2008, 1, 1).isoWeek == 1);
6988 
6989         assert(Date(2008, 12, 28).isoWeek == 52);
6990         assert(Date(2008, 12, 29).isoWeek == 1);
6991         assert(Date(2008, 12, 30).isoWeek == 1);
6992         assert(Date(2008, 12, 31).isoWeek == 1);
6993         assert(Date(2009, 1, 1).isoWeek == 1);
6994         assert(Date(2009, 1, 2).isoWeek == 1);
6995         assert(Date(2009, 1, 3).isoWeek == 1);
6996         assert(Date(2009, 1, 4).isoWeek == 1);
6997 
6998         // Test B.C.
6999         // The algorithm should work identically for both A.D. and B.C. since
7000         // it doesn't really take the year into account, so B.C. testing
7001         // probably isn't really needed.
7002         assert(Date(0, 12, 31).isoWeek == 52);
7003         assert(Date(0, 1, 4).isoWeek == 1);
7004         assert(Date(0, 1, 1).isoWeek == 52);
7005 
7006         const cdate = Date(1999, 7, 6);
7007         immutable idate = Date(1999, 7, 6);
7008         assert(cdate.isoWeek == 27);
7009         static assert(!__traits(compiles, cdate.isoWeek = 3));
7010         assert(idate.isoWeek == 27);
7011         static assert(!__traits(compiles, idate.isoWeek = 3));
7012     }
7013 
7014     /++
7015         The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
7016 
7017         May differ from $(LREF year) between 28 December and 4 January.
7018 
7019         See_Also:
7020             $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
7021       +/
7022     @property short isoWeekYear() const @safe pure nothrow
7023     {
7024         return isoWeekAndYear().isoWeekYear;
7025     }
7026 
7027     @safe unittest
7028     {
7029         // Test A.D.
7030         assert(Date(2009, 12, 28).isoWeekYear == 2009);
7031         assert(Date(2009, 12, 29).isoWeekYear == 2009);
7032         assert(Date(2009, 12, 30).isoWeekYear == 2009);
7033         assert(Date(2009, 12, 31).isoWeekYear == 2009);
7034         assert(Date(2010, 1, 1).isoWeekYear == 2009);
7035         assert(Date(2010, 1, 2).isoWeekYear == 2009);
7036         assert(Date(2010, 1, 3).isoWeekYear == 2009);
7037         assert(Date(2010, 1, 4).isoWeekYear == 2010);
7038         assert(Date(2010, 1, 5).isoWeekYear == 2010);
7039         assert(Date(2010, 1, 6).isoWeekYear == 2010);
7040         assert(Date(2010, 1, 7).isoWeekYear == 2010);
7041         assert(Date(2010, 1, 8).isoWeekYear == 2010);
7042         assert(Date(2010, 1, 9).isoWeekYear == 2010);
7043         assert(Date(2010, 1, 10).isoWeekYear == 2010);
7044         assert(Date(2010, 1, 11).isoWeekYear == 2010);
7045         assert(Date(2010, 12, 31).isoWeekYear == 2010);
7046 
7047         assert(Date(2004, 12, 26).isoWeekYear == 2004);
7048         assert(Date(2004, 12, 27).isoWeekYear == 2004);
7049         assert(Date(2004, 12, 28).isoWeekYear == 2004);
7050         assert(Date(2004, 12, 29).isoWeekYear == 2004);
7051         assert(Date(2004, 12, 30).isoWeekYear == 2004);
7052         assert(Date(2004, 12, 31).isoWeekYear == 2004);
7053         assert(Date(2005, 1, 1).isoWeekYear == 2004);
7054         assert(Date(2005, 1, 2).isoWeekYear == 2004);
7055         assert(Date(2005, 1, 3).isoWeekYear == 2005);
7056 
7057         assert(Date(2005, 12, 31).isoWeekYear == 2005);
7058         assert(Date(2007, 1, 1).isoWeekYear == 2007);
7059 
7060         assert(Date(2007, 12, 30).isoWeekYear == 2007);
7061         assert(Date(2007, 12, 31).isoWeekYear == 2008);
7062         assert(Date(2008, 1, 1).isoWeekYear == 2008);
7063 
7064         assert(Date(2008, 12, 28).isoWeekYear == 2008);
7065         assert(Date(2008, 12, 29).isoWeekYear == 2009);
7066         assert(Date(2008, 12, 30).isoWeekYear == 2009);
7067         assert(Date(2008, 12, 31).isoWeekYear == 2009);
7068         assert(Date(2009, 1, 1).isoWeekYear == 2009);
7069         assert(Date(2009, 1, 2).isoWeekYear == 2009);
7070         assert(Date(2009, 1, 3).isoWeekYear == 2009);
7071         assert(Date(2009, 1, 4).isoWeekYear == 2009);
7072 
7073         // Test B.C.
7074         assert(Date(0, 12, 31).isoWeekYear == 0);
7075         assert(Date(0, 1, 4).isoWeekYear == 0);
7076         assert(Date(0, 1, 1).isoWeekYear == -1);
7077 
7078         const cdate = Date(1999, 7, 6);
7079         immutable idate = Date(1999, 7, 6);
7080         assert(cdate.isoWeekYear == 1999);
7081         assert(idate.isoWeekYear == 1999);
7082     }
7083 
7084     static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
7085     {
7086         immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
7087         immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
7088 
7089         Date date;
7090         date._year = isoWeekYear;
7091         date._month = Month.jan;
7092         date._day = 3;
7093         immutable startOfYear = date.dayOfWeek;
7094         return date._addDays(dayOffset - startOfYear);
7095     }
7096 
7097     @safe unittest
7098     {
7099         // Test -30000 days to 30000 days for matching construction <-> deconstruction
7100         Date date = Date(1, 1, 1);
7101         date._addDays(-30_000);
7102         foreach (day; 0 .. 60_000)
7103         {
7104             const year = date.isoWeekYear;
7105             const dow = date.dayOfWeek;
7106             const isoWeek = date.isoWeek;
7107             const reversed = Date.fromISOWeek(year, isoWeek, dow);
7108             assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
7109             date = date._addDays(1);
7110         }
7111     }
7112 
7113 
7114     /++
7115         $(LREF Date) for the last day in the month that this $(LREF Date) is in.
7116       +/
7117     @property Date endOfMonth() const @safe pure nothrow
7118     {
7119         try
7120             return Date(_year, _month, maxDay(_year, _month));
7121         catch (Exception e)
7122             assert(0, "Date's constructor threw.");
7123     }
7124 
7125     ///
7126     @safe unittest
7127     {
7128         assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
7129         assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
7130         assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
7131         assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
7132     }
7133 
7134     @safe unittest
7135     {
7136         // Test A.D.
7137         assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
7138         assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
7139         assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
7140         assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
7141         assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
7142         assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
7143         assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
7144         assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
7145         assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
7146         assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
7147         assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
7148         assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
7149         assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
7150 
7151         // Test B.C.
7152         assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
7153         assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
7154         assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
7155         assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
7156         assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
7157         assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
7158         assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
7159         assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
7160         assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
7161         assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
7162         assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
7163         assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
7164         assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
7165 
7166         const cdate = Date(1999, 7, 6);
7167         immutable idate = Date(1999, 7, 6);
7168         static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
7169         static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
7170     }
7171 
7172 
7173     /++
7174         The last day in the month that this $(LREF Date) is in.
7175       +/
7176     @property ubyte daysInMonth() const @safe pure nothrow @nogc
7177     {
7178         return maxDay(_year, _month);
7179     }
7180 
7181     ///
7182     @safe unittest
7183     {
7184         assert(Date(1999, 1, 6).daysInMonth == 31);
7185         assert(Date(1999, 2, 7).daysInMonth == 28);
7186         assert(Date(2000, 2, 7).daysInMonth == 29);
7187         assert(Date(2000, 6, 4).daysInMonth == 30);
7188     }
7189 
7190     @safe unittest
7191     {
7192         // Test A.D.
7193         assert(Date(1999, 1, 1).daysInMonth == 31);
7194         assert(Date(1999, 2, 1).daysInMonth == 28);
7195         assert(Date(2000, 2, 1).daysInMonth == 29);
7196         assert(Date(1999, 3, 1).daysInMonth == 31);
7197         assert(Date(1999, 4, 1).daysInMonth == 30);
7198         assert(Date(1999, 5, 1).daysInMonth == 31);
7199         assert(Date(1999, 6, 1).daysInMonth == 30);
7200         assert(Date(1999, 7, 1).daysInMonth == 31);
7201         assert(Date(1999, 8, 1).daysInMonth == 31);
7202         assert(Date(1999, 9, 1).daysInMonth == 30);
7203         assert(Date(1999, 10, 1).daysInMonth == 31);
7204         assert(Date(1999, 11, 1).daysInMonth == 30);
7205         assert(Date(1999, 12, 1).daysInMonth == 31);
7206 
7207         // Test B.C.
7208         assert(Date(-1999, 1, 1).daysInMonth == 31);
7209         assert(Date(-1999, 2, 1).daysInMonth == 28);
7210         assert(Date(-2000, 2, 1).daysInMonth == 29);
7211         assert(Date(-1999, 3, 1).daysInMonth == 31);
7212         assert(Date(-1999, 4, 1).daysInMonth == 30);
7213         assert(Date(-1999, 5, 1).daysInMonth == 31);
7214         assert(Date(-1999, 6, 1).daysInMonth == 30);
7215         assert(Date(-1999, 7, 1).daysInMonth == 31);
7216         assert(Date(-1999, 8, 1).daysInMonth == 31);
7217         assert(Date(-1999, 9, 1).daysInMonth == 30);
7218         assert(Date(-1999, 10, 1).daysInMonth == 31);
7219         assert(Date(-1999, 11, 1).daysInMonth == 30);
7220         assert(Date(-1999, 12, 1).daysInMonth == 31);
7221 
7222         const cdate = Date(1999, 7, 6);
7223         immutable idate = Date(1999, 7, 6);
7224         static assert(!__traits(compiles, cdate.daysInMonth = 30));
7225         static assert(!__traits(compiles, idate.daysInMonth = 30));
7226     }
7227 
7228 
7229     /++
7230         Whether the current year is a date in A.D.
7231       +/
7232     @property bool isAD() const @safe pure nothrow @nogc
7233     {
7234         return _year > 0;
7235     }
7236 
7237     ///
7238     @safe unittest
7239     {
7240         assert(Date(1, 1, 1).isAD);
7241         assert(Date(2010, 12, 31).isAD);
7242         assert(!Date(0, 12, 31).isAD);
7243         assert(!Date(-2010, 1, 1).isAD);
7244     }
7245 
7246     @safe unittest
7247     {
7248         assert(Date(2010, 7, 4).isAD);
7249         assert(Date(1, 1, 1).isAD);
7250         assert(!Date(0, 1, 1).isAD);
7251         assert(!Date(-1, 1, 1).isAD);
7252         assert(!Date(-2010, 7, 4).isAD);
7253 
7254         const cdate = Date(1999, 7, 6);
7255         immutable idate = Date(1999, 7, 6);
7256         assert(cdate.isAD);
7257         assert(idate.isAD);
7258     }
7259 
7260 
7261     /++
7262         The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
7263         $(LREF Date) at noon (since the Julian day changes at noon).
7264       +/
7265     @property long julianDay() const @safe pure nothrow @nogc
7266     {
7267         return dayOfGregorianCal + 1_721_425;
7268     }
7269 
7270     @safe unittest
7271     {
7272         assert(Date(-4713, 11, 24).julianDay == 0);
7273         assert(Date(0, 12, 31).julianDay == 1_721_425);
7274         assert(Date(1, 1, 1).julianDay == 1_721_426);
7275         assert(Date(1582, 10, 15).julianDay == 2_299_161);
7276         assert(Date(1858, 11, 17).julianDay == 2_400_001);
7277         assert(Date(1982, 1, 4).julianDay == 2_444_974);
7278         assert(Date(1996, 3, 31).julianDay == 2_450_174);
7279         assert(Date(2010, 8, 24).julianDay == 2_455_433);
7280 
7281         const cdate = Date(1999, 7, 6);
7282         immutable idate = Date(1999, 7, 6);
7283         assert(cdate.julianDay == 2_451_366);
7284         assert(idate.julianDay == 2_451_366);
7285     }
7286 
7287 
7288     /++
7289         The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
7290         any time on this date (since, the modified Julian day changes at
7291         midnight).
7292       +/
7293     @property long modJulianDay() const @safe pure nothrow @nogc
7294     {
7295         return julianDay - 2_400_001;
7296     }
7297 
7298     @safe unittest
7299     {
7300         assert(Date(1858, 11, 17).modJulianDay == 0);
7301         assert(Date(2010, 8, 24).modJulianDay == 55_432);
7302 
7303         const cdate = Date(1999, 7, 6);
7304         immutable idate = Date(1999, 7, 6);
7305         assert(cdate.modJulianDay == 51_365);
7306         assert(idate.modJulianDay == 51_365);
7307     }
7308 
7309 
7310     /++
7311         Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
7312         If `writer` is set, the resulting string will be written directly
7313         to it.
7314 
7315         Params:
7316             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7317         Returns:
7318             A `string` when not using an output range; `void` otherwise.
7319       +/
7320     string toISOString() const @safe pure nothrow
7321     {
7322         import std.array : appender;
7323         auto w = appender!string();
7324         w.reserve(8);
7325         try
7326             toISOString(w);
7327         catch (Exception e)
7328             assert(0, "toISOString() threw.");
7329         return w.data;
7330     }
7331 
7332     ///
7333     @safe unittest
7334     {
7335         assert(Date(2010, 7, 4).toISOString() == "20100704");
7336         assert(Date(1998, 12, 25).toISOString() == "19981225");
7337         assert(Date(0, 1, 5).toISOString() == "00000105");
7338         assert(Date(-4, 1, 5).toISOString() == "-00040105");
7339     }
7340 
7341     @safe unittest
7342     {
7343         // Test A.D.
7344         assert(Date(9, 12, 4).toISOString() == "00091204");
7345         assert(Date(99, 12, 4).toISOString() == "00991204");
7346         assert(Date(999, 12, 4).toISOString() == "09991204");
7347         assert(Date(9999, 7, 4).toISOString() == "99990704");
7348         assert(Date(10000, 10, 20).toISOString() == "+100001020");
7349 
7350         // Test B.C.
7351         assert(Date(0, 12, 4).toISOString() == "00001204");
7352         assert(Date(-9, 12, 4).toISOString() == "-00091204");
7353         assert(Date(-99, 12, 4).toISOString() == "-00991204");
7354         assert(Date(-999, 12, 4).toISOString() == "-09991204");
7355         assert(Date(-9999, 7, 4).toISOString() == "-99990704");
7356         assert(Date(-10000, 10, 20).toISOString() == "-100001020");
7357 
7358         const cdate = Date(1999, 7, 6);
7359         immutable idate = Date(1999, 7, 6);
7360         assert(cdate.toISOString() == "19990706");
7361         assert(idate.toISOString() == "19990706");
7362     }
7363 
7364     /// ditto
7365     void toISOString(W)(ref W writer) const
7366     if (isOutputRange!(W, char))
7367     {
7368         import std.format.write : formattedWrite;
7369         if (_year >= 0)
7370         {
7371             if (_year < 10_000)
7372                 formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
7373             else
7374                 formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
7375         }
7376         else if (_year > -10_000)
7377             formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
7378         else
7379             formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
7380     }
7381 
7382     @safe pure unittest
7383     {
7384         import std.array : appender;
7385 
7386         auto w = appender!(char[])();
7387         Date(2010, 7, 4).toISOString(w);
7388         assert(w.data == "20100704");
7389         w.clear();
7390         Date(1998, 12, 25).toISOString(w);
7391         assert(w.data == "19981225");
7392     }
7393 
7394     /++
7395         Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
7396         If `writer` is set, the resulting string will be written directly
7397         to it.
7398 
7399         Params:
7400             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7401         Returns:
7402             A `string` when not using an output range; `void` otherwise.
7403       +/
7404     string toISOExtString() const @safe pure nothrow
7405     {
7406         import std.array : appender;
7407         auto w = appender!string();
7408         w.reserve(10);
7409         try
7410             toISOExtString(w);
7411         catch (Exception e)
7412             assert(0, "toISOExtString() threw.");
7413         return w.data;
7414     }
7415 
7416     ///
7417     @safe unittest
7418     {
7419         assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
7420         assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
7421         assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
7422         assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
7423     }
7424 
7425     @safe unittest
7426     {
7427         // Test A.D.
7428         assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
7429         assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
7430         assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
7431         assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
7432         assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
7433 
7434         // Test B.C.
7435         assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
7436         assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
7437         assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
7438         assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
7439         assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
7440         assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
7441 
7442         const cdate = Date(1999, 7, 6);
7443         immutable idate = Date(1999, 7, 6);
7444         assert(cdate.toISOExtString() == "1999-07-06");
7445         assert(idate.toISOExtString() == "1999-07-06");
7446     }
7447 
7448     /// ditto
7449     void toISOExtString(W)(ref W writer) const
7450     if (isOutputRange!(W, char))
7451     {
7452         import std.format.write : formattedWrite;
7453         if (_year >= 0)
7454         {
7455             if (_year < 10_000)
7456                 formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
7457             else
7458                 formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
7459         }
7460         else if (_year > -10_000)
7461             formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
7462         else
7463             formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
7464     }
7465 
7466     @safe pure unittest
7467     {
7468         import std.array : appender;
7469 
7470         auto w = appender!(char[])();
7471         Date(2010, 7, 4).toISOExtString(w);
7472         assert(w.data == "2010-07-04");
7473         w.clear();
7474         Date(-4, 1, 5).toISOExtString(w);
7475         assert(w.data == "-0004-01-05");
7476     }
7477 
7478     /++
7479         Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
7480         If `writer` is set, the resulting string will be written directly
7481         to it.
7482 
7483         Params:
7484             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7485         Returns:
7486             A `string` when not using an output range; `void` otherwise.
7487       +/
7488     string toSimpleString() const @safe pure nothrow
7489     {
7490         import std.array : appender;
7491         auto w = appender!string();
7492         w.reserve(11);
7493         try
7494             toSimpleString(w);
7495         catch (Exception e)
7496             assert(0, "toSimpleString() threw.");
7497         return w.data;
7498     }
7499 
7500     ///
7501     @safe unittest
7502     {
7503         assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
7504         assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
7505         assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
7506         assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
7507     }
7508 
7509     @safe unittest
7510     {
7511         // Test A.D.
7512         assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
7513         assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
7514         assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
7515         assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
7516         assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
7517 
7518         // Test B.C.
7519         assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
7520         assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
7521         assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
7522         assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
7523         assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
7524         assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
7525 
7526         const cdate = Date(1999, 7, 6);
7527         immutable idate = Date(1999, 7, 6);
7528         assert(cdate.toSimpleString() == "1999-Jul-06");
7529         assert(idate.toSimpleString() == "1999-Jul-06");
7530     }
7531 
7532     /// ditto
7533     void toSimpleString(W)(ref W writer) const
7534     if (isOutputRange!(W, char))
7535     {
7536         import std.format.write : formattedWrite;
7537         if (_year >= 0)
7538         {
7539             if (_year < 10_000)
7540                 formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
7541             else
7542                 formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
7543         }
7544         else if (_year > -10_000)
7545             formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
7546         else
7547             formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
7548     }
7549 
7550     @safe pure unittest
7551     {
7552         import std.array : appender;
7553 
7554         auto w = appender!(char[])();
7555         Date(9, 12, 4).toSimpleString(w);
7556         assert(w.data == "0009-Dec-04");
7557         w.clear();
7558         Date(-10000, 10, 20).toSimpleString(w);
7559         assert(w.data == "-10000-Oct-20");
7560     }
7561 
7562     /++
7563         Converts this $(LREF Date) to a string.
7564 
7565         This function exists to make it easy to convert a $(LREF Date) to a
7566         string for code that does not care what the exact format is - just that
7567         it presents the information in a clear manner. It also makes it easy to
7568         simply convert a $(LREF Date) to a string when using functions such as
7569         `to!string`, `format`, or `writeln` which use toString to convert
7570         user-defined types. So, it is unlikely that much code will call
7571         toString directly.
7572 
7573         The format of the string is purposefully unspecified, and code that
7574         cares about the format of the string should use `toISOString`,
7575         `toISOExtString`, `toSimpleString`, or some other custom formatting
7576         function that explicitly generates the format that the code needs. The
7577         reason is that the code is then clear about what format it's using,
7578         making it less error-prone to maintain the code and interact with other
7579         software that consumes the generated strings. It's for this same reason
7580         $(LREF Date) has no `fromString` function, whereas it does have
7581         `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
7582 
7583         The format returned by toString may or may not change in the future.
7584       +/
7585     string toString() const @safe pure nothrow
7586     {
7587         return toSimpleString();
7588     }
7589 
7590     @safe unittest
7591     {
7592         auto date = Date(1999, 7, 6);
7593         const cdate = Date(1999, 7, 6);
7594         immutable idate = Date(1999, 7, 6);
7595         assert(date.toString());
7596         assert(cdate.toString());
7597         assert(idate.toString());
7598     }
7599 
7600     /// ditto
7601     void toString(W)(ref W writer) const
7602     if (isOutputRange!(W, char))
7603     {
7604         toSimpleString(writer);
7605     }
7606 
7607     /++
7608         Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
7609         is stripped from the given string.
7610 
7611         Params:
7612             isoString = A string formatted in the ISO format for dates.
7613 
7614         Throws:
7615             $(REF DateTimeException,std,datetime,date) if the given string is
7616             not in the ISO format or if the resulting $(LREF Date) would not be
7617             valid.
7618       +/
7619     static Date fromISOString(S)(scope const S isoString) @safe pure
7620         if (isSomeString!S)
7621     {
7622         import std.algorithm.searching : startsWith;
7623         import std.conv : to, text, ConvException;
7624         import std.exception : enforce;
7625         import std.string : strip;
7626 
7627         auto str = isoString.strip;
7628 
7629         enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString));
7630 
7631         int day, month, year;
7632         auto yearStr = str[0 .. $ - 4];
7633 
7634         try
7635         {
7636             // using conversion to uint plus cast because it checks for +/-
7637             // for us quickly while throwing ConvException
7638             day = cast(int) to!uint(str[$ - 2 .. $]);
7639             month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
7640 
7641             if (yearStr.length > 4)
7642             {
7643                 enforce!DateTimeException(yearStr.startsWith('-', '+'),
7644                         text("Invalid ISO String: ", isoString));
7645                 year = to!int(yearStr);
7646             }
7647             else
7648             {
7649                 year = cast(int) to!uint(yearStr);
7650             }
7651         }
7652         catch (ConvException)
7653         {
7654             throw new DateTimeException(text("Invalid ISO String: ", isoString));
7655         }
7656 
7657         return Date(year, month, day);
7658     }
7659 
7660     ///
7661     @safe unittest
7662     {
7663         assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
7664         assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
7665         assert(Date.fromISOString("00000105") == Date(0, 1, 5));
7666         assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
7667         assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
7668     }
7669 
7670     @safe unittest
7671     {
7672         assertThrown!DateTimeException(Date.fromISOString(""));
7673         assertThrown!DateTimeException(Date.fromISOString("990704"));
7674         assertThrown!DateTimeException(Date.fromISOString("0100704"));
7675         assertThrown!DateTimeException(Date.fromISOString("2010070"));
7676         assertThrown!DateTimeException(Date.fromISOString("2010070 "));
7677         assertThrown!DateTimeException(Date.fromISOString("120100704"));
7678         assertThrown!DateTimeException(Date.fromISOString("-0100704"));
7679         assertThrown!DateTimeException(Date.fromISOString("+0100704"));
7680         assertThrown!DateTimeException(Date.fromISOString("2010070a"));
7681         assertThrown!DateTimeException(Date.fromISOString("20100a04"));
7682         assertThrown!DateTimeException(Date.fromISOString("2010a704"));
7683 
7684         assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
7685         assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
7686         assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
7687         assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
7688         assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
7689         assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
7690         assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
7691         assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
7692         assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
7693         assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
7694         assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
7695         assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
7696         assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
7697         assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
7698         assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
7699         assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
7700         assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
7701 
7702         assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
7703         assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
7704         assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
7705         assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
7706         assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
7707         assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
7708         assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
7709         assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
7710         assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
7711         assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
7712 
7713         assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
7714         assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
7715         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
7716         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
7717         assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
7718         assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
7719         assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
7720         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
7721         assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
7722         assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
7723         assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
7724 
7725         assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
7726         assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
7727 
7728         assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
7729         assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
7730         assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
7731         assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
7732         assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
7733         assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
7734     }
7735 
7736     // https://issues.dlang.org/show_bug.cgi?id=17801
7737     @safe unittest
7738     {
7739         import std.conv : to;
7740         import std.meta : AliasSeq;
7741         static foreach (C; AliasSeq!(char, wchar, dchar))
7742         {
7743             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7744                 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
7745         }
7746     }
7747 
7748 
7749     /++
7750         Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
7751         Whitespace is stripped from the given string.
7752 
7753         Params:
7754             isoExtString = A string formatted in the ISO Extended format for
7755                            dates.
7756 
7757         Throws:
7758             $(REF DateTimeException,std,datetime,date) if the given string is
7759             not in the ISO Extended format or if the resulting $(LREF Date)
7760             would not be valid.
7761       +/
7762     static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
7763         if (isSomeString!(S))
7764     {
7765         import std.algorithm.searching : startsWith;
7766         import std.conv : to, ConvException;
7767         import std.format : format;
7768         import std.string : strip;
7769 
7770         auto str = strip(isoExtString);
7771         short year;
7772         ubyte month, day;
7773 
7774         if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
7775             throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
7776 
7777         auto yearStr = str[0 .. $-6];
7778         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7779         if ((yearStr.length > 4) != signAtBegining)
7780         {
7781             throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
7782         }
7783 
7784         try
7785         {
7786             day = to!ubyte(str[$-2 .. $]);
7787             month = to!ubyte(str[$-5 .. $-3]);
7788             year = to!short(yearStr);
7789         }
7790         catch (ConvException)
7791         {
7792             throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
7793         }
7794 
7795         return Date(year, month, day);
7796     }
7797 
7798     ///
7799     @safe unittest
7800     {
7801         assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
7802         assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
7803         assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
7804         assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
7805         assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
7806     }
7807 
7808     @safe unittest
7809     {
7810         assertThrown!DateTimeException(Date.fromISOExtString(""));
7811         assertThrown!DateTimeException(Date.fromISOExtString("990704"));
7812         assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
7813         assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
7814         assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
7815         assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
7816         assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
7817         assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
7818         assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
7819         assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
7820         assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
7821 
7822         assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
7823         assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
7824         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
7825         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
7826         assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
7827         assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
7828         assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
7829         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
7830         assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
7831         assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
7832         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
7833         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
7834         assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
7835         assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
7836         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
7837         assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
7838         assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
7839 
7840         assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
7841         assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
7842         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
7843         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
7844         assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
7845         assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
7846         assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
7847         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
7848         assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
7849         assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
7850 
7851         assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
7852         assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
7853         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
7854         assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
7855         assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
7856         assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
7857         assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
7858         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
7859         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
7860         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
7861         assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
7862 
7863         assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
7864         assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
7865 
7866         assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
7867         assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
7868         assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
7869         assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
7870         assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
7871         assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
7872     }
7873 
7874     // https://issues.dlang.org/show_bug.cgi?id=17801
7875     @safe unittest
7876     {
7877         import std.conv : to;
7878         import std.meta : AliasSeq;
7879         static foreach (C; AliasSeq!(char, wchar, dchar))
7880         {
7881             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7882                 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
7883         }
7884     }
7885 
7886 
7887     /++
7888         Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
7889         Whitespace is stripped from the given string.
7890 
7891         Params:
7892             simpleString = A string formatted in the way that toSimpleString
7893                            formats dates.
7894 
7895         Throws:
7896             $(REF DateTimeException,std,datetime,date) if the given string is
7897             not in the correct format or if the resulting $(LREF Date) would not
7898             be valid.
7899       +/
7900     static Date fromSimpleString(S)(scope const S simpleString) @safe pure
7901         if (isSomeString!(S))
7902     {
7903         import std.algorithm.searching : startsWith;
7904         import std.conv : to, ConvException;
7905         import std.format : format;
7906         import std.string : strip;
7907 
7908         auto str = strip(simpleString);
7909 
7910         if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
7911             throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
7912 
7913         int year;
7914         uint day;
7915         auto month = monthFromString(str[$ - 6 .. $ - 3]);
7916         auto yearStr = str[0 .. $ - 7];
7917         auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7918         if ((yearStr.length > 4) != signAtBegining)
7919         {
7920             throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
7921         }
7922 
7923         try
7924         {
7925             day = to!uint(str[$ - 2 .. $]);
7926             year = to!int(yearStr);
7927         }
7928         catch (ConvException)
7929         {
7930             throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
7931         }
7932 
7933         return Date(year, month, day);
7934     }
7935 
7936     ///
7937     @safe unittest
7938     {
7939         assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
7940         assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
7941         assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
7942         assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
7943         assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
7944     }
7945 
7946     @safe unittest
7947     {
7948         assertThrown!DateTimeException(Date.fromSimpleString(""));
7949         assertThrown!DateTimeException(Date.fromSimpleString("990704"));
7950         assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
7951         assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
7952         assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
7953         assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
7954         assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
7955         assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
7956         assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
7957         assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
7958         assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
7959 
7960         assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
7961         assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
7962         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
7963         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
7964         assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
7965         assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
7966         assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
7967         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
7968         assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
7969         assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
7970         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
7971         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
7972         assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
7973         assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
7974         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
7975         assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
7976         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
7977 
7978         assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
7979         assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
7980         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
7981         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
7982         assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
7983         assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
7984         assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
7985         assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
7986         assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
7987         assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
7988 
7989         assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
7990         assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
7991         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
7992         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
7993         assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
7994         assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
7995         assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
7996         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
7997         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
7998         assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
7999         assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
8000 
8001         assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
8002         assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
8003 
8004         assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
8005         assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
8006         assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
8007         assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
8008         assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
8009         assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
8010     }
8011 
8012     // https://issues.dlang.org/show_bug.cgi?id=17801
8013     @safe unittest
8014     {
8015         import std.conv : to;
8016         import std.meta : AliasSeq;
8017         static foreach (C; AliasSeq!(char, wchar, dchar))
8018         {
8019             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
8020                 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
8021         }
8022     }
8023 
8024 
8025     /++
8026         Returns the $(LREF Date) farthest in the past which is representable by
8027         $(LREF Date).
8028       +/
8029     @property static Date min() @safe pure nothrow @nogc
8030     {
8031         auto date = Date.init;
8032         date._year = short.min;
8033         date._month = Month.jan;
8034         date._day = 1;
8035 
8036         return date;
8037     }
8038 
8039     @safe unittest
8040     {
8041         assert(Date.min.year < 0);
8042         assert(Date.min < Date.max);
8043     }
8044 
8045 
8046     /++
8047         Returns the $(LREF Date) farthest in the future which is representable
8048         by $(LREF Date).
8049       +/
8050     @property static Date max() @safe pure nothrow @nogc
8051     {
8052         auto date = Date.init;
8053         date._year = short.max;
8054         date._month = Month.dec;
8055         date._day = 31;
8056 
8057         return date;
8058     }
8059 
8060     @safe unittest
8061     {
8062         assert(Date.max.year > 0);
8063         assert(Date.max > Date.min);
8064     }
8065 
8066 
8067 private:
8068 
8069     /+
8070         Whether the given values form a valid date.
8071 
8072         Params:
8073             year  = The year to test.
8074             month = The month of the Gregorian Calendar to test.
8075             day   = The day of the month to test.
8076      +/
8077     static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
8078     {
8079         if (!valid!"months"(month))
8080             return false;
8081         return valid!"days"(year, month, day);
8082     }
8083 
8084 
8085 package:
8086 
8087     /+
8088         Adds the given number of days to this $(LREF Date). A negative number
8089         will subtract.
8090 
8091         The month will be adjusted along with the day if the number of days
8092         added (or subtracted) would overflow (or underflow) the current month.
8093         The year will be adjusted along with the month if the increase (or
8094         decrease) to the month would cause it to overflow (or underflow) the
8095         current year.
8096 
8097         `_addDays(numDays)` is effectively equivalent to
8098         $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
8099 
8100         Params:
8101             days = The number of days to add to this Date.
8102       +/
8103     ref Date _addDays(long days) return @safe pure nothrow @nogc
8104     {
8105         dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
8106         return this;
8107     }
8108 
8109     @safe unittest
8110     {
8111         // Test A.D.
8112         {
8113             auto date = Date(1999, 2, 28);
8114             date._addDays(1);
8115             assert(date == Date(1999, 3, 1));
8116             date._addDays(-1);
8117             assert(date == Date(1999, 2, 28));
8118         }
8119 
8120         {
8121             auto date = Date(2000, 2, 28);
8122             date._addDays(1);
8123             assert(date == Date(2000, 2, 29));
8124             date._addDays(1);
8125             assert(date == Date(2000, 3, 1));
8126             date._addDays(-1);
8127             assert(date == Date(2000, 2, 29));
8128         }
8129 
8130         {
8131             auto date = Date(1999, 6, 30);
8132             date._addDays(1);
8133             assert(date == Date(1999, 7, 1));
8134             date._addDays(-1);
8135             assert(date == Date(1999, 6, 30));
8136         }
8137 
8138         {
8139             auto date = Date(1999, 7, 31);
8140             date._addDays(1);
8141             assert(date == Date(1999, 8, 1));
8142             date._addDays(-1);
8143             assert(date == Date(1999, 7, 31));
8144         }
8145 
8146         {
8147             auto date = Date(1999, 1, 1);
8148             date._addDays(-1);
8149             assert(date == Date(1998, 12, 31));
8150             date._addDays(1);
8151             assert(date == Date(1999, 1, 1));
8152         }
8153 
8154         {
8155             auto date = Date(1999, 7, 6);
8156             date._addDays(9);
8157             assert(date == Date(1999, 7, 15));
8158             date._addDays(-11);
8159             assert(date == Date(1999, 7, 4));
8160             date._addDays(30);
8161             assert(date == Date(1999, 8, 3));
8162             date._addDays(-3);
8163             assert(date == Date(1999, 7, 31));
8164         }
8165 
8166         {
8167             auto date = Date(1999, 7, 6);
8168             date._addDays(365);
8169             assert(date == Date(2000, 7, 5));
8170             date._addDays(-365);
8171             assert(date == Date(1999, 7, 6));
8172             date._addDays(366);
8173             assert(date == Date(2000, 7, 6));
8174             date._addDays(730);
8175             assert(date == Date(2002, 7, 6));
8176             date._addDays(-1096);
8177             assert(date == Date(1999, 7, 6));
8178         }
8179 
8180         // Test B.C.
8181         {
8182             auto date = Date(-1999, 2, 28);
8183             date._addDays(1);
8184             assert(date == Date(-1999, 3, 1));
8185             date._addDays(-1);
8186             assert(date == Date(-1999, 2, 28));
8187         }
8188 
8189         {
8190             auto date = Date(-2000, 2, 28);
8191             date._addDays(1);
8192             assert(date == Date(-2000, 2, 29));
8193             date._addDays(1);
8194             assert(date == Date(-2000, 3, 1));
8195             date._addDays(-1);
8196             assert(date == Date(-2000, 2, 29));
8197         }
8198 
8199         {
8200             auto date = Date(-1999, 6, 30);
8201             date._addDays(1);
8202             assert(date == Date(-1999, 7, 1));
8203             date._addDays(-1);
8204             assert(date == Date(-1999, 6, 30));
8205         }
8206 
8207         {
8208             auto date = Date(-1999, 7, 31);
8209             date._addDays(1);
8210             assert(date == Date(-1999, 8, 1));
8211             date._addDays(-1);
8212             assert(date == Date(-1999, 7, 31));
8213         }
8214 
8215         {
8216             auto date = Date(-1999, 1, 1);
8217             date._addDays(-1);
8218             assert(date == Date(-2000, 12, 31));
8219             date._addDays(1);
8220             assert(date == Date(-1999, 1, 1));
8221         }
8222 
8223         {
8224             auto date = Date(-1999, 7, 6);
8225             date._addDays(9);
8226             assert(date == Date(-1999, 7, 15));
8227             date._addDays(-11);
8228             assert(date == Date(-1999, 7, 4));
8229             date._addDays(30);
8230             assert(date == Date(-1999, 8, 3));
8231             date._addDays(-3);
8232         }
8233 
8234         {
8235             auto date = Date(-1999, 7, 6);
8236             date._addDays(365);
8237             assert(date == Date(-1998, 7, 6));
8238             date._addDays(-365);
8239             assert(date == Date(-1999, 7, 6));
8240             date._addDays(366);
8241             assert(date == Date(-1998, 7, 7));
8242             date._addDays(730);
8243             assert(date == Date(-1996, 7, 6));
8244             date._addDays(-1096);
8245             assert(date == Date(-1999, 7, 6));
8246         }
8247 
8248         // Test Both
8249         {
8250             auto date = Date(1, 7, 6);
8251             date._addDays(-365);
8252             assert(date == Date(0, 7, 6));
8253             date._addDays(365);
8254             assert(date == Date(1, 7, 6));
8255             date._addDays(-731);
8256             assert(date == Date(-1, 7, 6));
8257             date._addDays(730);
8258             assert(date == Date(1, 7, 5));
8259         }
8260 
8261         const cdate = Date(1999, 7, 6);
8262         immutable idate = Date(1999, 7, 6);
8263         static assert(!__traits(compiles, cdate._addDays(12)));
8264         static assert(!__traits(compiles, idate._addDays(12)));
8265     }
8266 
8267 
8268     @safe pure invariant()
8269     {
8270         import std.format : format;
8271         assert(valid!"months"(_month),
8272                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8273         assert(valid!"days"(_year, _month, _day),
8274                format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8275     }
8276 
8277     short _year  = 1;
8278     Month _month = Month.jan;
8279     ubyte _day   = 1;
8280 }
8281 
8282 ///
8283 @safe pure unittest
8284 {
8285     import core.time : days;
8286 
8287     auto d = Date(2000, 6, 1);
8288 
8289     assert(d.dayOfYear == 153);
8290     assert(d.dayOfWeek == DayOfWeek.thu);
8291 
8292     d += 10.days;
8293     assert(d == Date(2000, 6, 11));
8294 
8295     assert(d.toISOExtString() == "2000-06-11");
8296     assert(d.toISOString() == "20000611");
8297     assert(d.toSimpleString() == "2000-Jun-11");
8298 
8299     assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
8300     assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
8301     assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
8302 }
8303 
8304 
8305 /++
8306     Represents a time of day with hours, minutes, and seconds. It uses 24 hour
8307     time.
8308 +/
8309 struct TimeOfDay
8310 {
8311 public:
8312 
8313     /++
8314         Params:
8315             hour   = Hour of the day [0 - 24$(RPAREN).
8316             minute = Minute of the hour [0 - 60$(RPAREN).
8317             second = Second of the minute [0 - 60$(RPAREN).
8318 
8319         Throws:
8320             $(REF DateTimeException,std,datetime,date) if the resulting
8321             $(LREF TimeOfDay) would be not be valid.
8322      +/
8323     this(int hour, int minute, int second = 0) @safe pure
8324     {
8325         enforceValid!"hours"(hour);
8326         enforceValid!"minutes"(minute);
8327         enforceValid!"seconds"(second);
8328 
8329         _hour   = cast(ubyte) hour;
8330         _minute = cast(ubyte) minute;
8331         _second = cast(ubyte) second;
8332     }
8333 
8334     @safe unittest
8335     {
8336         assert(TimeOfDay(0, 0) == TimeOfDay.init);
8337 
8338         {
8339             auto tod = TimeOfDay(0, 0);
8340             assert(tod._hour == 0);
8341             assert(tod._minute == 0);
8342             assert(tod._second == 0);
8343         }
8344 
8345         {
8346             auto tod = TimeOfDay(12, 30, 33);
8347             assert(tod._hour == 12);
8348             assert(tod._minute == 30);
8349             assert(tod._second == 33);
8350         }
8351 
8352         {
8353             auto tod = TimeOfDay(23, 59, 59);
8354             assert(tod._hour == 23);
8355             assert(tod._minute == 59);
8356             assert(tod._second == 59);
8357         }
8358 
8359         assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
8360         assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
8361         assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
8362     }
8363 
8364 
8365     /++
8366         Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
8367 
8368         Returns:
8369             $(BOOKTABLE,
8370             $(TR $(TD this &lt; rhs) $(TD &lt; 0))
8371             $(TR $(TD this == rhs) $(TD 0))
8372             $(TR $(TD this &gt; rhs) $(TD &gt; 0))
8373             )
8374      +/
8375     int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
8376     {
8377         if (_hour < rhs._hour)
8378             return -1;
8379         if (_hour > rhs._hour)
8380             return 1;
8381 
8382         if (_minute < rhs._minute)
8383             return -1;
8384         if (_minute > rhs._minute)
8385             return 1;
8386 
8387         if (_second < rhs._second)
8388             return -1;
8389         if (_second > rhs._second)
8390             return 1;
8391 
8392         return 0;
8393     }
8394 
8395     @safe unittest
8396     {
8397         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
8398 
8399         assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
8400         assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
8401         assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
8402         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8403 
8404         assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
8405         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
8406 
8407         assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
8408         assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8409 
8410         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8411         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8412         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
8413         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8414         assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
8415         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
8416 
8417         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8418         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
8419         assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
8420         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8421 
8422         assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8423         assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
8424 
8425         const ctod = TimeOfDay(12, 30, 33);
8426         immutable itod = TimeOfDay(12, 30, 33);
8427         assert(ctod.opCmp(itod) == 0);
8428         assert(itod.opCmp(ctod) == 0);
8429     }
8430 
8431 
8432     /++
8433         Hours past midnight.
8434      +/
8435     @property ubyte hour() const @safe pure nothrow @nogc
8436     {
8437         return _hour;
8438     }
8439 
8440     @safe unittest
8441     {
8442         assert(TimeOfDay.init.hour == 0);
8443         assert(TimeOfDay(12, 0, 0).hour == 12);
8444 
8445         const ctod = TimeOfDay(12, 0, 0);
8446         immutable itod = TimeOfDay(12, 0, 0);
8447         assert(ctod.hour == 12);
8448         assert(itod.hour == 12);
8449     }
8450 
8451 
8452     /++
8453         Hours past midnight.
8454 
8455         Params:
8456             hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
8457 
8458         Throws:
8459             $(REF DateTimeException,std,datetime,date) if the given hour would
8460             result in an invalid $(LREF TimeOfDay).
8461      +/
8462     @property void hour(int hour) @safe pure
8463     {
8464         enforceValid!"hours"(hour);
8465         _hour = cast(ubyte) hour;
8466     }
8467 
8468     @safe unittest
8469     {
8470         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
8471 
8472         auto tod = TimeOfDay(0, 0, 0);
8473         tod.hour = 12;
8474         assert(tod == TimeOfDay(12, 0, 0));
8475 
8476         const ctod = TimeOfDay(0, 0, 0);
8477         immutable itod = TimeOfDay(0, 0, 0);
8478         static assert(!__traits(compiles, ctod.hour = 12));
8479         static assert(!__traits(compiles, itod.hour = 12));
8480     }
8481 
8482 
8483     /++
8484         Minutes past the hour.
8485      +/
8486     @property ubyte minute() const @safe pure nothrow @nogc
8487     {
8488         return _minute;
8489     }
8490 
8491     @safe unittest
8492     {
8493         assert(TimeOfDay.init.minute == 0);
8494         assert(TimeOfDay(0, 30, 0).minute == 30);
8495 
8496         const ctod = TimeOfDay(0, 30, 0);
8497         immutable itod = TimeOfDay(0, 30, 0);
8498         assert(ctod.minute == 30);
8499         assert(itod.minute == 30);
8500     }
8501 
8502 
8503     /++
8504         Minutes past the hour.
8505 
8506         Params:
8507             minute = The minute to set this $(LREF TimeOfDay)'s minute to.
8508 
8509         Throws:
8510             $(REF DateTimeException,std,datetime,date) if the given minute
8511             would result in an invalid $(LREF TimeOfDay).
8512      +/
8513     @property void minute(int minute) @safe pure
8514     {
8515         enforceValid!"minutes"(minute);
8516         _minute = cast(ubyte) minute;
8517     }
8518 
8519     @safe unittest
8520     {
8521         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
8522 
8523         auto tod = TimeOfDay(0, 0, 0);
8524         tod.minute = 30;
8525         assert(tod == TimeOfDay(0, 30, 0));
8526 
8527         const ctod = TimeOfDay(0, 0, 0);
8528         immutable itod = TimeOfDay(0, 0, 0);
8529         static assert(!__traits(compiles, ctod.minute = 30));
8530         static assert(!__traits(compiles, itod.minute = 30));
8531     }
8532 
8533 
8534     /++
8535         Seconds past the minute.
8536      +/
8537     @property ubyte second() const @safe pure nothrow @nogc
8538     {
8539         return _second;
8540     }
8541 
8542     @safe unittest
8543     {
8544         assert(TimeOfDay.init.second == 0);
8545         assert(TimeOfDay(0, 0, 33).second == 33);
8546 
8547         const ctod = TimeOfDay(0, 0, 33);
8548         immutable itod = TimeOfDay(0, 0, 33);
8549         assert(ctod.second == 33);
8550         assert(itod.second == 33);
8551     }
8552 
8553 
8554     /++
8555         Seconds past the minute.
8556 
8557         Params:
8558             second = The second to set this $(LREF TimeOfDay)'s second to.
8559 
8560         Throws:
8561             $(REF DateTimeException,std,datetime,date) if the given second
8562             would result in an invalid $(LREF TimeOfDay).
8563      +/
8564     @property void second(int second) @safe pure
8565     {
8566         enforceValid!"seconds"(second);
8567         _second = cast(ubyte) second;
8568     }
8569 
8570     @safe unittest
8571     {
8572         assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
8573 
8574         auto tod = TimeOfDay(0, 0, 0);
8575         tod.second = 33;
8576         assert(tod == TimeOfDay(0, 0, 33));
8577 
8578         const ctod = TimeOfDay(0, 0, 0);
8579         immutable itod = TimeOfDay(0, 0, 0);
8580         static assert(!__traits(compiles, ctod.second = 33));
8581         static assert(!__traits(compiles, itod.second = 33));
8582     }
8583 
8584 
8585     /++
8586         Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
8587         negative number will subtract.
8588 
8589         The difference between rolling and adding is that rolling does not
8590         affect larger units. For instance, rolling a $(LREF TimeOfDay)
8591         one hours's worth of minutes gets the exact same
8592         $(LREF TimeOfDay).
8593 
8594         Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
8595 
8596         Params:
8597             units = The units to add.
8598             value = The number of $(D_PARAM units) to add to this
8599                     $(LREF TimeOfDay).
8600 
8601         Returns:
8602             A reference to the `TimeOfDay` (`this`).
8603       +/
8604     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8605         if (units == "hours")
8606     {
8607         import core.time : dur;
8608         return this += dur!"hours"(value);
8609     }
8610 
8611     ///
8612     @safe unittest
8613     {
8614         auto tod1 = TimeOfDay(7, 12, 0);
8615         tod1.roll!"hours"(1);
8616         assert(tod1 == TimeOfDay(8, 12, 0));
8617 
8618         auto tod2 = TimeOfDay(7, 12, 0);
8619         tod2.roll!"hours"(-1);
8620         assert(tod2 == TimeOfDay(6, 12, 0));
8621 
8622         auto tod3 = TimeOfDay(23, 59, 0);
8623         tod3.roll!"minutes"(1);
8624         assert(tod3 == TimeOfDay(23, 0, 0));
8625 
8626         auto tod4 = TimeOfDay(0, 0, 0);
8627         tod4.roll!"minutes"(-1);
8628         assert(tod4 == TimeOfDay(0, 59, 0));
8629 
8630         auto tod5 = TimeOfDay(23, 59, 59);
8631         tod5.roll!"seconds"(1);
8632         assert(tod5 == TimeOfDay(23, 59, 0));
8633 
8634         auto tod6 = TimeOfDay(0, 0, 0);
8635         tod6.roll!"seconds"(-1);
8636         assert(tod6 == TimeOfDay(0, 0, 59));
8637     }
8638 
8639     @safe unittest
8640     {
8641         auto tod = TimeOfDay(12, 27, 2);
8642         tod.roll!"hours"(22).roll!"hours"(-7);
8643         assert(tod == TimeOfDay(3, 27, 2));
8644 
8645         const ctod = TimeOfDay(0, 0, 0);
8646         immutable itod = TimeOfDay(0, 0, 0);
8647         static assert(!__traits(compiles, ctod.roll!"hours"(53)));
8648         static assert(!__traits(compiles, itod.roll!"hours"(53)));
8649     }
8650 
8651 
8652     /// ditto
8653     ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8654         if (units == "minutes" || units == "seconds")
8655     {
8656         import std.format : format;
8657 
8658         enum memberVarStr = units[0 .. $ - 1];
8659         value %= 60;
8660         mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
8661 
8662         if (value < 0)
8663         {
8664             if (newVal < 0)
8665                 newVal += 60;
8666         }
8667         else if (newVal >= 60)
8668             newVal -= 60;
8669 
8670         mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
8671         return this;
8672     }
8673 
8674     // Test roll!"minutes"().
8675     @safe unittest
8676     {
8677         static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
8678         {
8679             orig.roll!"minutes"(minutes);
8680             assert(orig == expected);
8681         }
8682 
8683         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8684         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
8685         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
8686         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
8687         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
8688         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
8689         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
8690         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
8691         testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
8692         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
8693         testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
8694         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8695         testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
8696         testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
8697         testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
8698 
8699         testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
8700         testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
8701         testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
8702         testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
8703         testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
8704         testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
8705         testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
8706         testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
8707 
8708         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
8709         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
8710         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
8711         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
8712         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
8713         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
8714         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
8715         testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
8716         testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
8717         testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
8718         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8719         testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
8720         testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
8721         testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
8722 
8723         testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
8724         testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
8725         testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
8726         testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
8727         testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
8728         testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
8729         testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
8730         testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
8731 
8732         testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
8733         testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
8734         testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
8735 
8736         testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
8737         testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
8738         testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
8739 
8740         testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
8741         testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
8742         testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
8743 
8744         testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
8745         testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
8746         testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
8747 
8748         auto tod = TimeOfDay(12, 27, 2);
8749         tod.roll!"minutes"(97).roll!"minutes"(-102);
8750         assert(tod == TimeOfDay(12, 22, 2));
8751 
8752         const ctod = TimeOfDay(0, 0, 0);
8753         immutable itod = TimeOfDay(0, 0, 0);
8754         static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
8755         static assert(!__traits(compiles, itod.roll!"minutes"(7)));
8756     }
8757 
8758     // Test roll!"seconds"().
8759     @safe unittest
8760     {
8761         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
8762         {
8763             orig.roll!"seconds"(seconds);
8764             assert(orig == expected);
8765         }
8766 
8767         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8768         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
8769         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
8770         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
8771         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
8772         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
8773         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
8774         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
8775         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
8776         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
8777         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
8778         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
8779         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8780         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
8781 
8782         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
8783         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
8784         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
8785         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
8786         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
8787         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
8788         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
8789         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
8790 
8791         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
8792         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
8793         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
8794         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
8795         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
8796         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
8797         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
8798         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
8799         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
8800         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
8801         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
8802         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8803         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
8804 
8805         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
8806         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
8807         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
8808 
8809         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
8810         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
8811         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
8812 
8813         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
8814         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
8815         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
8816 
8817         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
8818         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
8819         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
8820 
8821         auto tod = TimeOfDay(12, 27, 2);
8822         tod.roll!"seconds"(105).roll!"seconds"(-77);
8823         assert(tod == TimeOfDay(12, 27, 30));
8824 
8825         const ctod = TimeOfDay(0, 0, 0);
8826         immutable itod = TimeOfDay(0, 0, 0);
8827         static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
8828         static assert(!__traits(compiles, itod.roll!"seconds"(7)));
8829     }
8830 
8831 
8832     import core.time : Duration;
8833     /++
8834         Gives the result of adding or subtracting a $(REF Duration, core,time)
8835         from this $(LREF TimeOfDay).
8836 
8837         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8838         are
8839 
8840         $(BOOKTABLE,
8841         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8842         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8843         )
8844 
8845         Params:
8846             duration = The $(REF Duration, core,time) to add to or subtract from
8847                        this $(LREF TimeOfDay).
8848       +/
8849     TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
8850         if (op == "+" || op == "-")
8851     {
8852         TimeOfDay retval = this;
8853         immutable seconds = duration.total!"seconds";
8854         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
8855     }
8856 
8857     ///
8858     @safe unittest
8859     {
8860         import core.time : hours, minutes, seconds;
8861 
8862         assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
8863         assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
8864         assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
8865         assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
8866 
8867         assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
8868         assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
8869         assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
8870         assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
8871     }
8872 
8873     @safe unittest
8874     {
8875         auto tod = TimeOfDay(12, 30, 33);
8876 
8877         import core.time : dur;
8878         assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8879         assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8880         assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8881         assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8882         assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8883         assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8884 
8885         assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8886         assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8887         assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8888         assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8889         assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8890         assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8891 
8892         assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8893         assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8894         assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8895         assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8896         assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8897         assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8898 
8899         assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8900         assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8901         assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8902         assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8903         assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8904         assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8905 
8906         auto duration = dur!"hours"(11);
8907         const ctod = TimeOfDay(12, 30, 33);
8908         immutable itod = TimeOfDay(12, 30, 33);
8909         assert(tod + duration == TimeOfDay(23, 30, 33));
8910         assert(ctod + duration == TimeOfDay(23, 30, 33));
8911         assert(itod + duration == TimeOfDay(23, 30, 33));
8912 
8913         assert(tod - duration == TimeOfDay(1, 30, 33));
8914         assert(ctod - duration == TimeOfDay(1, 30, 33));
8915         assert(itod - duration == TimeOfDay(1, 30, 33));
8916     }
8917 
8918 
8919     /++
8920         Gives the result of adding or subtracting a $(REF Duration, core,time)
8921         from this $(LREF TimeOfDay), as well as assigning the result to this
8922         $(LREF TimeOfDay).
8923 
8924         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8925         are
8926 
8927         $(BOOKTABLE,
8928         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8929         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8930         )
8931 
8932         Params:
8933             duration = The $(REF Duration, core,time) to add to or subtract from
8934                        this $(LREF TimeOfDay).
8935       +/
8936     ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
8937         if (op == "+" || op == "-")
8938     {
8939         immutable seconds = duration.total!"seconds";
8940         mixin("return _addSeconds(" ~ op ~ "seconds);");
8941     }
8942 
8943     @safe unittest
8944     {
8945         import core.time : dur;
8946         auto duration = dur!"hours"(12);
8947 
8948         assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8949         assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8950         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8951         assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8952         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8953         assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8954 
8955         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8956         assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8957         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8958         assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8959         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8960         assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8961 
8962         assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8963         assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8964         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8965         assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8966         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8967         assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8968 
8969         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8970         assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8971         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8972         assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8973         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8974         assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8975 
8976         auto tod = TimeOfDay(19, 17, 22);
8977         (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
8978         assert(tod == TimeOfDay(17, 15, 59));
8979 
8980         const ctod = TimeOfDay(12, 33, 30);
8981         immutable itod = TimeOfDay(12, 33, 30);
8982         static assert(!__traits(compiles, ctod += duration));
8983         static assert(!__traits(compiles, itod += duration));
8984         static assert(!__traits(compiles, ctod -= duration));
8985         static assert(!__traits(compiles, itod -= duration));
8986     }
8987 
8988 
8989     /++
8990         Gives the difference between two $(LREF TimeOfDay)s.
8991 
8992         The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8993         are
8994 
8995         $(BOOKTABLE,
8996         $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
8997         )
8998 
8999         Params:
9000             rhs = The $(LREF TimeOfDay) to subtract from this one.
9001       +/
9002     Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
9003         if (op == "-")
9004     {
9005         immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
9006         immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
9007 
9008         import core.time : dur;
9009         return dur!"seconds"(lhsSec - rhsSec);
9010     }
9011 
9012     @safe unittest
9013     {
9014         auto tod = TimeOfDay(12, 30, 33);
9015 
9016         import core.time : dur;
9017         assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
9018         assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
9019         assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
9020         assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
9021         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
9022         assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
9023         assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
9024         assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
9025 
9026         const ctod = TimeOfDay(12, 30, 33);
9027         immutable itod = TimeOfDay(12, 30, 33);
9028         assert(tod - tod == Duration.zero);
9029         assert(ctod - tod == Duration.zero);
9030         assert(itod - tod == Duration.zero);
9031 
9032         assert(tod - ctod == Duration.zero);
9033         assert(ctod - ctod == Duration.zero);
9034         assert(itod - ctod == Duration.zero);
9035 
9036         assert(tod - itod == Duration.zero);
9037         assert(ctod - itod == Duration.zero);
9038         assert(itod - itod == Duration.zero);
9039     }
9040 
9041 
9042     /++
9043         Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
9044         If `writer` is set, the resulting string will be written directly to it.
9045 
9046         Params:
9047             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9048         Returns:
9049             A `string` when not using an output range; `void` otherwise.
9050       +/
9051     string toISOString() const @safe pure nothrow
9052     {
9053         import std.array : appender;
9054         auto w = appender!string();
9055         w.reserve(6);
9056         try
9057             toISOString(w);
9058         catch (Exception e)
9059             assert(0, "toISOString() threw.");
9060         return w.data;
9061     }
9062 
9063     /// ditto
9064     void toISOString(W)(ref W writer) const
9065     if (isOutputRange!(W, char))
9066     {
9067         import std.format.write : formattedWrite;
9068         formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
9069     }
9070 
9071     ///
9072     @safe unittest
9073     {
9074         assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
9075         assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
9076     }
9077 
9078     @safe unittest
9079     {
9080         auto tod = TimeOfDay(12, 30, 33);
9081         const ctod = TimeOfDay(12, 30, 33);
9082         immutable itod = TimeOfDay(12, 30, 33);
9083         assert(tod.toISOString() == "123033");
9084         assert(ctod.toISOString() == "123033");
9085         assert(itod.toISOString() == "123033");
9086     }
9087 
9088 
9089     /++
9090         Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
9091         If `writer` is set, the resulting string will be written directly to it.
9092 
9093         Params:
9094             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9095         Returns:
9096             A `string` when not using an output range; `void` otherwise.
9097       +/
9098     string toISOExtString() const @safe pure nothrow
9099     {
9100         import std.array : appender;
9101         auto w = appender!string();
9102         w.reserve(8);
9103         try
9104             toISOExtString(w);
9105         catch (Exception e)
9106             assert(0, "toISOExtString() threw.");
9107         return w.data;
9108     }
9109 
9110     /// ditto
9111     void toISOExtString(W)(ref W writer) const
9112     if (isOutputRange!(W, char))
9113     {
9114         import std.format.write : formattedWrite;
9115         formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
9116     }
9117 
9118     ///
9119     @safe unittest
9120     {
9121         assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
9122         assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
9123     }
9124 
9125     @safe unittest
9126     {
9127         auto tod = TimeOfDay(12, 30, 33);
9128         const ctod = TimeOfDay(12, 30, 33);
9129         immutable itod = TimeOfDay(12, 30, 33);
9130         assert(tod.toISOExtString() == "12:30:33");
9131         assert(ctod.toISOExtString() == "12:30:33");
9132         assert(itod.toISOExtString() == "12:30:33");
9133     }
9134 
9135 
9136     /++
9137         Converts this TimeOfDay to a string.
9138 
9139         This function exists to make it easy to convert a $(LREF TimeOfDay) to a
9140         string for code that does not care what the exact format is - just that
9141         it presents the information in a clear manner. It also makes it easy to
9142         simply convert a $(LREF TimeOfDay) to a string when using functions such
9143         as `to!string`, `format`, or `writeln` which use toString to convert
9144         user-defined types. So, it is unlikely that much code will call
9145         toString directly.
9146 
9147         The format of the string is purposefully unspecified, and code that
9148         cares about the format of the string should use `toISOString`,
9149         `toISOExtString`, or some other custom formatting function that
9150         explicitly generates the format that the code needs. The reason is that
9151         the code is then clear about what format it's using, making it less
9152         error-prone to maintain the code and interact with other software that
9153         consumes the generated strings. It's for this same reason that
9154         $(LREF TimeOfDay) has no `fromString` function, whereas it does have
9155         `fromISOString` and `fromISOExtString`.
9156 
9157         The format returned by toString may or may not change in the future.
9158 
9159         Params:
9160             writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9161         Returns:
9162             A `string` when not using an output range; `void` otherwise.
9163       +/
9164     string toString() const @safe pure nothrow
9165     {
9166         return toISOExtString();
9167     }
9168 
9169     /// ditto
9170     void toString(W)(ref W writer) const
9171     if (isOutputRange!(W, char))
9172     {
9173         toISOExtString(writer);
9174     }
9175 
9176     @safe unittest
9177     {
9178         auto tod = TimeOfDay(12, 30, 33);
9179         const ctod = TimeOfDay(12, 30, 33);
9180         immutable itod = TimeOfDay(12, 30, 33);
9181         assert(tod.toString());
9182         assert(ctod.toString());
9183         assert(itod.toString());
9184     }
9185 
9186 
9187     /++
9188         Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
9189         Whitespace is stripped from the given string.
9190 
9191         Params:
9192             isoString = A string formatted in the ISO format for times.
9193 
9194         Throws:
9195             $(REF DateTimeException,std,datetime,date) if the given string is
9196             not in the ISO format or if the resulting $(LREF TimeOfDay) would
9197             not be valid.
9198       +/
9199     static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
9200         if (isSomeString!S)
9201     {
9202         import std.conv : to, text, ConvException;
9203         import std.exception : enforce;
9204         import std.string : strip;
9205 
9206         int hours, minutes, seconds;
9207         auto str = strip(isoString);
9208 
9209         enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString));
9210 
9211         try
9212         {
9213             // cast to int from uint is used because it checks for
9214             // non digits without extra loops
9215             hours = cast(int) to!uint(str[0 .. 2]);
9216             minutes = cast(int) to!uint(str[2 .. 4]);
9217             seconds = cast(int) to!uint(str[4 .. $]);
9218         }
9219         catch (ConvException)
9220         {
9221             throw new DateTimeException(text("Invalid ISO String: ", isoString));
9222         }
9223 
9224         return TimeOfDay(hours, minutes, seconds);
9225     }
9226 
9227     ///
9228     @safe unittest
9229     {
9230         assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
9231         assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
9232         assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
9233     }
9234 
9235     @safe unittest
9236     {
9237         assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
9238         assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
9239         assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
9240         assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
9241         assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
9242         assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
9243         assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
9244         assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
9245         assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
9246         assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
9247         assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
9248         assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
9249         assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
9250         assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
9251         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
9252         assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
9253         assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
9254         assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
9255         assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
9256         assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
9257         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
9258         assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
9259 
9260         assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
9261         assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
9262         assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
9263         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
9264         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
9265         assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
9266         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
9267         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
9268         assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
9269         assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
9270         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
9271         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
9272         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
9273         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
9274         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
9275         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
9276         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
9277         assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
9278         assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
9279         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
9280         assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
9281         assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
9282         assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
9283         assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
9284         assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
9285         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
9286         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
9287 
9288         assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
9289 
9290         assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
9291         assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
9292         assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
9293         assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
9294         assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
9295         assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
9296     }
9297 
9298     // https://issues.dlang.org/show_bug.cgi?id=17801
9299     @safe unittest
9300     {
9301         import std.conv : to;
9302         import std.meta : AliasSeq;
9303         static foreach (C; AliasSeq!(char, wchar, dchar))
9304         {
9305             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9306                 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
9307         }
9308     }
9309 
9310 
9311     /++
9312         Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
9313         Whitespace is stripped from the given string.
9314 
9315         Params:
9316             isoExtString = A string formatted in the ISO Extended format for
9317             times.
9318 
9319         Throws:
9320             $(REF DateTimeException,std,datetime,date) if the given string is
9321             not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
9322             would not be valid.
9323       +/
9324     static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
9325         if (isSomeString!S)
9326     {
9327         import std.conv : ConvException, text, to;
9328         import std.string : strip;
9329 
9330         auto str = strip(isoExtString);
9331         int hours, minutes, seconds;
9332 
9333         if (str.length != 8 || str[2] != ':' || str[5] != ':')
9334             throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
9335 
9336         try
9337         {
9338             // cast to int from uint is used because it checks for
9339             // non digits without extra loops
9340             hours = cast(int) to!uint(str[0 .. 2]);
9341             minutes = cast(int) to!uint(str[3 .. 5]);
9342             seconds = cast(int) to!uint(str[6 .. $]);
9343         }
9344         catch (ConvException)
9345         {
9346             throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
9347         }
9348 
9349         return TimeOfDay(hours, minutes, seconds);
9350     }
9351 
9352     ///
9353     @safe unittest
9354     {
9355         assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
9356         assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
9357         assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
9358     }
9359 
9360     @safe unittest
9361     {
9362         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
9363         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
9364         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
9365         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
9366         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
9367         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
9368         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
9369         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
9370         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
9371         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
9372         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
9373         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
9374         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
9375         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
9376         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
9377         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
9378         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
9379         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
9380         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
9381         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
9382         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
9383         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
9384 
9385         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
9386         assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
9387         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
9388         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
9389         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
9390         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
9391         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
9392         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
9393         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
9394         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
9395         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
9396         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
9397         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
9398         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
9399         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
9400         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
9401         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
9402         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
9403         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
9404         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
9405         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
9406         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
9407         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
9408         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
9409         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
9410         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
9411         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
9412 
9413         assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
9414 
9415         assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
9416         assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
9417         assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
9418         assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
9419         assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
9420         assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
9421     }
9422 
9423     // https://issues.dlang.org/show_bug.cgi?id=17801
9424     @safe unittest
9425     {
9426         import std.conv : to;
9427         import std.meta : AliasSeq;
9428         static foreach (C; AliasSeq!(char, wchar, dchar))
9429         {
9430             static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9431                 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
9432         }
9433     }
9434 
9435 
9436     /++
9437         Returns midnight.
9438       +/
9439     @property static TimeOfDay min() @safe pure nothrow @nogc
9440     {
9441         return TimeOfDay.init;
9442     }
9443 
9444     @safe unittest
9445     {
9446         assert(TimeOfDay.min.hour == 0);
9447         assert(TimeOfDay.min.minute == 0);
9448         assert(TimeOfDay.min.second == 0);
9449         assert(TimeOfDay.min < TimeOfDay.max);
9450     }
9451 
9452 
9453     /++
9454         Returns one second short of midnight.
9455       +/
9456     @property static TimeOfDay max() @safe pure nothrow @nogc
9457     {
9458         auto tod = TimeOfDay.init;
9459         tod._hour = maxHour;
9460         tod._minute = maxMinute;
9461         tod._second = maxSecond;
9462 
9463         return tod;
9464     }
9465 
9466     @safe unittest
9467     {
9468         assert(TimeOfDay.max.hour == 23);
9469         assert(TimeOfDay.max.minute == 59);
9470         assert(TimeOfDay.max.second == 59);
9471         assert(TimeOfDay.max > TimeOfDay.min);
9472     }
9473 
9474 
9475 private:
9476 
9477     /+
9478         Add seconds to the time of day. Negative values will subtract. If the
9479         number of seconds overflows (or underflows), then the seconds will wrap,
9480         increasing (or decreasing) the number of minutes accordingly. If the
9481         number of minutes overflows (or underflows), then the minutes will wrap.
9482         If the number of minutes overflows(or underflows), then the hour will
9483         wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
9484 
9485         Params:
9486             seconds = The number of seconds to add to this TimeOfDay.
9487       +/
9488     ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
9489     {
9490         import core.time : convert;
9491         long hnsecs = convert!("seconds", "hnsecs")(seconds);
9492         hnsecs += convert!("hours", "hnsecs")(_hour);
9493         hnsecs += convert!("minutes", "hnsecs")(_minute);
9494         hnsecs += convert!("seconds", "hnsecs")(_second);
9495 
9496         hnsecs %= convert!("days", "hnsecs")(1);
9497 
9498         if (hnsecs < 0)
9499             hnsecs += convert!("days", "hnsecs")(1);
9500 
9501         immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
9502         immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9503         immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
9504 
9505         _hour = cast(ubyte) newHours;
9506         _minute = cast(ubyte) newMinutes;
9507         _second = cast(ubyte) newSeconds;
9508 
9509         return this;
9510     }
9511 
9512     @safe unittest
9513     {
9514         static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
9515         {
9516             orig._addSeconds(seconds);
9517             assert(orig == expected);
9518         }
9519 
9520         testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
9521         testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
9522         testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
9523         testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
9524         testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
9525         testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
9526         testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
9527         testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
9528         testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
9529         testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
9530         testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
9531         testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
9532         testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
9533         testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
9534 
9535         testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
9536         testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
9537         testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
9538         testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
9539         testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
9540         testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
9541         testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
9542         testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
9543 
9544         testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
9545         testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
9546         testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
9547         testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
9548         testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
9549         testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
9550         testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
9551         testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
9552         testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
9553         testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
9554         testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
9555         testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
9556         testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
9557 
9558         testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
9559         testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
9560         testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
9561         testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
9562         testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
9563         testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
9564 
9565         testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
9566         testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
9567         testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
9568 
9569         testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
9570         testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
9571         testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
9572 
9573         testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
9574         testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
9575         testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
9576 
9577         testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
9578         testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
9579         testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
9580 
9581         const ctod = TimeOfDay(0, 0, 0);
9582         immutable itod = TimeOfDay(0, 0, 0);
9583         static assert(!__traits(compiles, ctod._addSeconds(7)));
9584         static assert(!__traits(compiles, itod._addSeconds(7)));
9585     }
9586 
9587 
9588     /+
9589         Whether the given values form a valid $(LREF TimeOfDay).
9590      +/
9591     static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
9592     {
9593         return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
9594     }
9595 
9596 
9597     @safe pure invariant()
9598     {
9599         import std.format : format;
9600         assert(_valid(_hour, _minute, _second),
9601                format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
9602     }
9603 
9604 
9605 package:
9606 
9607     ubyte _hour;
9608     ubyte _minute;
9609     ubyte _second;
9610 
9611     enum ubyte maxHour   = 24 - 1;
9612     enum ubyte maxMinute = 60 - 1;
9613     enum ubyte maxSecond = 60 - 1;
9614 }
9615 
9616 ///
9617 @safe pure unittest
9618 {
9619     import core.time : minutes, seconds;
9620 
9621     auto t = TimeOfDay(12, 30, 0);
9622 
9623     t += 10.minutes + 100.seconds;
9624     assert(t == TimeOfDay(12, 41, 40));
9625 
9626     assert(t.toISOExtString() == "12:41:40");
9627     assert(t.toISOString() == "124140");
9628 
9629     assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
9630     assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
9631 }
9632 
9633 /++
9634     Returns whether the given value is valid for the given unit type when in a
9635     time point. Naturally, a duration is not held to a particular range, but
9636     the values in a time point are (e.g. a month must be in the range of
9637     1 - 12 inclusive).
9638 
9639     Params:
9640         units = The units of time to validate.
9641         value = The number to validate.
9642   +/
9643 bool valid(string units)(int value) @safe pure nothrow @nogc
9644 if (units == "months" ||
9645     units == "hours" ||
9646     units == "minutes" ||
9647     units == "seconds")
9648 {
9649     static if (units == "months")
9650         return value >= Month.jan && value <= Month.dec;
9651     else static if (units == "hours")
9652         return value >= 0 && value <= 23;
9653     else static if (units == "minutes")
9654         return value >= 0 && value <= 59;
9655     else static if (units == "seconds")
9656         return value >= 0 && value <= 59;
9657 }
9658 
9659 ///
9660 @safe unittest
9661 {
9662     assert(valid!"hours"(12));
9663     assert(!valid!"hours"(32));
9664     assert(valid!"months"(12));
9665     assert(!valid!"months"(13));
9666 }
9667 
9668 /++
9669     Returns whether the given day is valid for the given year and month.
9670 
9671     Params:
9672         units = The units of time to validate.
9673         year  = The year of the day to validate.
9674         month = The month of the day to validate (January is 1).
9675         day   = The day to validate.
9676   +/
9677 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9678 if (units == "days")
9679 {
9680     return day > 0 && day <= maxDay(year, month);
9681 }
9682 
9683 ///
9684 @safe pure nothrow @nogc unittest
9685 {
9686     assert(valid!"days"(2016, 2, 29));
9687     assert(!valid!"days"(2016, 2, 30));
9688     assert(valid!"days"(2017, 2, 20));
9689     assert(!valid!"days"(2017, 2, 29));
9690 }
9691 
9692 
9693 /++
9694     Params:
9695         units = The units of time to validate.
9696         value = The number to validate.
9697         file  = The file that the $(LREF DateTimeException) will list if thrown.
9698         line  = The line number that the $(LREF DateTimeException) will list if
9699                 thrown.
9700 
9701     Throws:
9702         $(LREF DateTimeException) if `valid!units(value)` is false.
9703   +/
9704 void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9705 if (units == "months" ||
9706     units == "hours" ||
9707     units == "minutes" ||
9708     units == "seconds")
9709 {
9710     import std.format : format;
9711 
9712     static if (units == "months")
9713     {
9714         if (!valid!units(value))
9715             throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
9716     }
9717     else static if (units == "hours")
9718     {
9719         if (!valid!units(value))
9720             throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
9721     }
9722     else static if (units == "minutes")
9723     {
9724         if (!valid!units(value))
9725             throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
9726     }
9727     else static if (units == "seconds")
9728     {
9729         if (!valid!units(value))
9730             throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
9731     }
9732 }
9733 
9734 ///
9735 @safe pure unittest
9736 {
9737     import std.exception : assertThrown, assertNotThrown;
9738 
9739     assertNotThrown(enforceValid!"months"(10));
9740     assertNotThrown(enforceValid!"seconds"(40));
9741 
9742     assertThrown!DateTimeException(enforceValid!"months"(0));
9743     assertThrown!DateTimeException(enforceValid!"hours"(24));
9744     assertThrown!DateTimeException(enforceValid!"minutes"(60));
9745     assertThrown!DateTimeException(enforceValid!"seconds"(60));
9746 }
9747 
9748 
9749 /++
9750     Because the validity of the day number depends on both on the year
9751     and month of which the day is occurring, take all three variables
9752     to validate the day.
9753 
9754     Params:
9755         units = The units of time to validate.
9756         year  = The year of the day to validate.
9757         month = The month of the day to validate.
9758         day   = The day to validate.
9759         file  = The file that the $(LREF DateTimeException) will list if thrown.
9760         line  = The line number that the $(LREF DateTimeException) will list if
9761                 thrown.
9762 
9763     Throws:
9764         $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
9765   +/
9766 void enforceValid(string units)
9767                  (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9768 if (units == "days")
9769 {
9770     import std.format : format;
9771     if (!valid!"days"(year, month, day))
9772         throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
9773 }
9774 
9775 ///
9776 @safe pure unittest
9777 {
9778     import std.exception : assertThrown, assertNotThrown;
9779 
9780     assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
9781     // leap year
9782     assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
9783 
9784     assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
9785     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
9786     assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
9787 }
9788 
9789 
9790 /++
9791     Returns the number of days from the current day of the week to the given
9792     day of the week. If they are the same, then the result is 0.
9793 
9794     Params:
9795         currDoW = The current day of the week.
9796         dow     = The day of the week to get the number of days to.
9797   +/
9798 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
9799 {
9800     if (currDoW == dow)
9801         return 0;
9802     if (currDoW < dow)
9803         return dow - currDoW;
9804     return DayOfWeek.sat - currDoW + dow + 1;
9805 }
9806 
9807 ///
9808 @safe pure nothrow @nogc unittest
9809 {
9810     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9811     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9812     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9813 }
9814 
9815 @safe unittest
9816 {
9817     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
9818     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
9819     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
9820     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
9821     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
9822     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
9823     assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
9824 
9825     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9826     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9827     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
9828     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9829     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
9830     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
9831     assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
9832 
9833     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
9834     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
9835     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
9836     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
9837     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
9838     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
9839     assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
9840 
9841     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
9842     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
9843     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
9844     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
9845     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
9846     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
9847     assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
9848 
9849     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
9850     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
9851     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
9852     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
9853     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
9854     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
9855     assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
9856 
9857     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
9858     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
9859     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
9860     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
9861     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
9862     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
9863     assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
9864 
9865     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
9866     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
9867     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
9868     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
9869     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
9870     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
9871     assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
9872 }
9873 
9874 
9875 /++
9876     Returns the number of months from the current months of the year to the
9877     given month of the year. If they are the same, then the result is 0.
9878 
9879     Params:
9880         currMonth = The current month of the year.
9881         month     = The month of the year to get the number of months to.
9882   +/
9883 int monthsToMonth(int currMonth, int month) @safe pure
9884 {
9885     enforceValid!"months"(currMonth);
9886     enforceValid!"months"(month);
9887 
9888     if (currMonth == month)
9889         return 0;
9890     if (currMonth < month)
9891         return month - currMonth;
9892     return Month.dec - currMonth + month;
9893 }
9894 
9895 ///
9896 @safe pure unittest
9897 {
9898     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9899     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9900     assert(monthsToMonth(Month.jul, Month.oct) == 3);
9901 }
9902 
9903 @safe unittest
9904 {
9905     assert(monthsToMonth(Month.jan, Month.jan) == 0);
9906     assert(monthsToMonth(Month.jan, Month.feb) == 1);
9907     assert(monthsToMonth(Month.jan, Month.mar) == 2);
9908     assert(monthsToMonth(Month.jan, Month.apr) == 3);
9909     assert(monthsToMonth(Month.jan, Month.may) == 4);
9910     assert(monthsToMonth(Month.jan, Month.jun) == 5);
9911     assert(monthsToMonth(Month.jan, Month.jul) == 6);
9912     assert(monthsToMonth(Month.jan, Month.aug) == 7);
9913     assert(monthsToMonth(Month.jan, Month.sep) == 8);
9914     assert(monthsToMonth(Month.jan, Month.oct) == 9);
9915     assert(monthsToMonth(Month.jan, Month.nov) == 10);
9916     assert(monthsToMonth(Month.jan, Month.dec) == 11);
9917 
9918     assert(monthsToMonth(Month.may, Month.jan) == 8);
9919     assert(monthsToMonth(Month.may, Month.feb) == 9);
9920     assert(monthsToMonth(Month.may, Month.mar) == 10);
9921     assert(monthsToMonth(Month.may, Month.apr) == 11);
9922     assert(monthsToMonth(Month.may, Month.may) == 0);
9923     assert(monthsToMonth(Month.may, Month.jun) == 1);
9924     assert(monthsToMonth(Month.may, Month.jul) == 2);
9925     assert(monthsToMonth(Month.may, Month.aug) == 3);
9926     assert(monthsToMonth(Month.may, Month.sep) == 4);
9927     assert(monthsToMonth(Month.may, Month.oct) == 5);
9928     assert(monthsToMonth(Month.may, Month.nov) == 6);
9929     assert(monthsToMonth(Month.may, Month.dec) == 7);
9930 
9931     assert(monthsToMonth(Month.oct, Month.jan) == 3);
9932     assert(monthsToMonth(Month.oct, Month.feb) == 4);
9933     assert(monthsToMonth(Month.oct, Month.mar) == 5);
9934     assert(monthsToMonth(Month.oct, Month.apr) == 6);
9935     assert(monthsToMonth(Month.oct, Month.may) == 7);
9936     assert(monthsToMonth(Month.oct, Month.jun) == 8);
9937     assert(monthsToMonth(Month.oct, Month.jul) == 9);
9938     assert(monthsToMonth(Month.oct, Month.aug) == 10);
9939     assert(monthsToMonth(Month.oct, Month.sep) == 11);
9940     assert(monthsToMonth(Month.oct, Month.oct) == 0);
9941     assert(monthsToMonth(Month.oct, Month.nov) == 1);
9942     assert(monthsToMonth(Month.oct, Month.dec) == 2);
9943 
9944     assert(monthsToMonth(Month.dec, Month.jan) == 1);
9945     assert(monthsToMonth(Month.dec, Month.feb) == 2);
9946     assert(monthsToMonth(Month.dec, Month.mar) == 3);
9947     assert(monthsToMonth(Month.dec, Month.apr) == 4);
9948     assert(monthsToMonth(Month.dec, Month.may) == 5);
9949     assert(monthsToMonth(Month.dec, Month.jun) == 6);
9950     assert(monthsToMonth(Month.dec, Month.jul) == 7);
9951     assert(monthsToMonth(Month.dec, Month.aug) == 8);
9952     assert(monthsToMonth(Month.dec, Month.sep) == 9);
9953     assert(monthsToMonth(Month.dec, Month.oct) == 10);
9954     assert(monthsToMonth(Month.dec, Month.nov) == 11);
9955     assert(monthsToMonth(Month.dec, Month.dec) == 0);
9956 }
9957 
9958 
9959 /++
9960     Whether the given Gregorian Year is a leap year.
9961 
9962     Params:
9963         year = The year to to be tested.
9964  +/
9965 bool yearIsLeapYear(int year) @safe pure nothrow @nogc
9966 {
9967     if (year % 400 == 0)
9968         return true;
9969     if (year % 100 == 0)
9970         return false;
9971     return year % 4 == 0;
9972 }
9973 
9974 ///
9975 @safe unittest
9976 {
9977     foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
9978     {
9979         assert(!yearIsLeapYear(year));
9980         assert(!yearIsLeapYear(-year));
9981     }
9982 
9983     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
9984     {
9985         assert(yearIsLeapYear(year));
9986         assert(yearIsLeapYear(-year));
9987     }
9988 }
9989 
9990 @safe unittest
9991 {
9992     import std.format : format;
9993     foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
9994                     2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
9995     {
9996         assert(!yearIsLeapYear(year), format("year: %s.", year));
9997         assert(!yearIsLeapYear(-year), format("year: %s.", year));
9998     }
9999 
10000     foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
10001     {
10002         assert(yearIsLeapYear(year), format("year: %s.", year));
10003         assert(yearIsLeapYear(-year), format("year: %s.", year));
10004     }
10005 }
10006 
10007 
10008 /++
10009     Whether the given type defines all of the necessary functions for it to
10010     function as a time point.
10011 
10012     1. `T` must define a static property named `min` which is the smallest
10013        value of `T` as `Unqual!T`.
10014 
10015     2. `T` must define a static property named `max` which is the largest
10016        value of `T` as `Unqual!T`.
10017 
10018     3. `T` must define an `opBinary` for addition and subtraction that
10019        accepts $(REF Duration, core,time) and returns `Unqual!T`.
10020 
10021     4. `T` must define an `opOpAssign` for addition and subtraction that
10022        accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
10023 
10024     5. `T` must define a `opBinary` for subtraction which accepts `T`
10025        and returns $(REF Duration, core,time).
10026   +/
10027 template isTimePoint(T)
10028 {
10029     import core.time : Duration;
10030     import std.traits : FunctionAttribute, functionAttributes, Unqual;
10031 
10032     enum isTimePoint = hasMin &&
10033                        hasMax &&
10034                        hasOverloadedOpBinaryWithDuration &&
10035                        hasOverloadedOpAssignWithDuration &&
10036                        hasOverloadedOpBinaryWithSelf &&
10037                        !is(U == Duration);
10038 
10039 private:
10040 
10041     alias U = Unqual!T;
10042 
10043     enum hasMin = __traits(hasMember, T, "min") &&
10044                   is(typeof(T.min) == U) &&
10045                   is(typeof({static assert(__traits(isStaticFunction, T.min));}));
10046 
10047     enum hasMax = __traits(hasMember, T, "max") &&
10048                   is(typeof(T.max) == U) &&
10049                   is(typeof({static assert(__traits(isStaticFunction, T.max));}));
10050 
10051     enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
10052                                              is(typeof(T.init - Duration.init) == U);
10053 
10054     enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
10055                                              is(typeof(U.init -= Duration.init) == U) &&
10056                                              is(typeof(
10057                                              {
10058                                                  alias add = U.opOpAssign!"+";
10059                                                  alias sub = U.opOpAssign!"-";
10060                                                  alias FA = FunctionAttribute;
10061                                                  static assert((functionAttributes!add & FA.ref_) != 0);
10062                                                  static assert((functionAttributes!sub & FA.ref_) != 0);
10063                                              }));
10064 
10065     enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
10066 }
10067 
10068 ///
10069 @safe unittest
10070 {
10071     import core.time : Duration;
10072     import std.datetime.interval : Interval;
10073     import std.datetime.systime : SysTime;
10074 
10075     static assert(isTimePoint!Date);
10076     static assert(isTimePoint!DateTime);
10077     static assert(isTimePoint!SysTime);
10078     static assert(isTimePoint!TimeOfDay);
10079 
10080     static assert(!isTimePoint!int);
10081     static assert(!isTimePoint!Duration);
10082     static assert(!isTimePoint!(Interval!SysTime));
10083 }
10084 
10085 @safe unittest
10086 {
10087     import core.time;
10088     import std.datetime.interval;
10089     import std.datetime.systime;
10090     import std.meta : AliasSeq;
10091 
10092     static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
10093     {
10094         static assert(isTimePoint!(const TP), TP.stringof);
10095         static assert(isTimePoint!(immutable TP), TP.stringof);
10096     }
10097 
10098     static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
10099         static assert(!isTimePoint!T, T.stringof);
10100 }
10101 
10102 
10103 /++
10104     Whether all of the given strings are valid units of time.
10105 
10106     `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
10107     can handle precision greater than hnsecs, and the few functions in core.time
10108     which deal with "nsecs" deal with it explicitly.
10109   +/
10110 bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
10111 {
10112     import std.algorithm.searching : canFind;
10113     foreach (str; units)
10114     {
10115         if (!canFind(timeStrings[], str))
10116             return false;
10117     }
10118     return true;
10119 }
10120 
10121 ///
10122 @safe @nogc nothrow unittest
10123 {
10124     assert(validTimeUnits("msecs", "seconds", "minutes"));
10125     assert(validTimeUnits("days", "weeks", "months"));
10126     assert(!validTimeUnits("ms", "seconds", "minutes"));
10127 }
10128 
10129 
10130 /++
10131     Compares two time unit strings. `"years"` are the largest units and
10132     `"hnsecs"` are the smallest.
10133 
10134     Returns:
10135         $(BOOKTABLE,
10136         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10137         $(TR $(TD this == rhs) $(TD 0))
10138         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10139         )
10140 
10141     Throws:
10142         $(LREF DateTimeException) if either of the given strings is not a valid
10143         time unit string.
10144  +/
10145 int cmpTimeUnits(string lhs, string rhs) @safe pure
10146 {
10147     import std.algorithm.searching : countUntil;
10148     import std.exception : enforce;
10149     import std.format : format;
10150 
10151     immutable indexOfLHS = countUntil(timeStrings, lhs);
10152     immutable indexOfRHS = countUntil(timeStrings, rhs);
10153 
10154     enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
10155     enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
10156 
10157     if (indexOfLHS < indexOfRHS)
10158         return -1;
10159     if (indexOfLHS > indexOfRHS)
10160         return 1;
10161 
10162     return 0;
10163 }
10164 
10165 ///
10166 @safe pure unittest
10167 {
10168     import std.exception : assertThrown;
10169 
10170     assert(cmpTimeUnits("hours", "hours") == 0);
10171     assert(cmpTimeUnits("hours", "weeks") < 0);
10172     assert(cmpTimeUnits("months", "seconds") > 0);
10173 
10174     assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
10175 }
10176 
10177 @safe unittest
10178 {
10179     foreach (i, outerUnits; timeStrings)
10180     {
10181         assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
10182 
10183         // For some reason, $ won't compile.
10184         foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
10185             assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
10186     }
10187 
10188     foreach (i, outerUnits; timeStrings)
10189     {
10190         foreach (innerUnits; timeStrings[0 .. i])
10191             assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
10192     }
10193 }
10194 
10195 
10196 /++
10197     Compares two time unit strings at compile time. `"years"` are the largest
10198     units and `"hnsecs"` are the smallest.
10199 
10200     This template is used instead of `cmpTimeUnits` because exceptions
10201     can't be thrown at compile time and `cmpTimeUnits` must enforce that
10202     the strings it's given are valid time unit strings. This template uses a
10203     template constraint instead.
10204 
10205     Returns:
10206         $(BOOKTABLE,
10207         $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10208         $(TR $(TD this == rhs) $(TD 0))
10209         $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10210         )
10211  +/
10212 template CmpTimeUnits(string lhs, string rhs)
10213 if (validTimeUnits(lhs, rhs))
10214 {
10215     enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
10216 }
10217 
10218 ///
10219 @safe pure unittest
10220 {
10221     static assert(CmpTimeUnits!("years", "weeks") > 0);
10222     static assert(CmpTimeUnits!("days", "days") == 0);
10223     static assert(CmpTimeUnits!("seconds", "hours") < 0);
10224 }
10225 
10226 // Helper function for CmpTimeUnits.
10227 private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
10228 {
10229     import std.algorithm.searching : countUntil;
10230     auto tstrings = timeStrings;
10231     immutable indexOfLHS = countUntil(tstrings, lhs);
10232     immutable indexOfRHS = countUntil(tstrings, rhs);
10233 
10234     if (indexOfLHS < indexOfRHS)
10235         return -1;
10236     if (indexOfLHS > indexOfRHS)
10237         return 1;
10238 
10239     return 0;
10240 }
10241 
10242 @safe unittest
10243 {
10244     static foreach (i; 0 .. timeStrings.length)
10245     {
10246         static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
10247 
10248         static foreach (next; timeStrings[i + 1 .. $])
10249             static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
10250 
10251         static foreach (prev; timeStrings[0 .. i])
10252             static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
10253     }
10254 }
10255 
10256 
10257 package:
10258 
10259 
10260 /+
10261     Array of the short (three letter) names of each month.
10262   +/
10263 immutable string[12] _monthNames = ["Jan",
10264                                     "Feb",
10265                                     "Mar",
10266                                     "Apr",
10267                                     "May",
10268                                     "Jun",
10269                                     "Jul",
10270                                     "Aug",
10271                                     "Sep",
10272                                     "Oct",
10273                                     "Nov",
10274                                     "Dec"];
10275 
10276 /+
10277     The maximum valid Day in the given month in the given year.
10278 
10279     Params:
10280         year  = The year to get the day for.
10281         month = The month of the Gregorian Calendar to get the day for.
10282  +/
10283 ubyte maxDay(int year, int month) @safe pure nothrow @nogc
10284 in
10285 {
10286     assert(valid!"months"(month));
10287 }
10288 do
10289 {
10290     switch (month)
10291     {
10292         case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
10293             return 31;
10294         case Month.feb:
10295             return yearIsLeapYear(year) ? 29 : 28;
10296         case Month.apr, Month.jun, Month.sep, Month.nov:
10297             return 30;
10298         default:
10299             assert(0, "Invalid month.");
10300     }
10301 }
10302 
10303 @safe unittest
10304 {
10305     // Test A.D.
10306     assert(maxDay(1999, 1) == 31);
10307     assert(maxDay(1999, 2) == 28);
10308     assert(maxDay(1999, 3) == 31);
10309     assert(maxDay(1999, 4) == 30);
10310     assert(maxDay(1999, 5) == 31);
10311     assert(maxDay(1999, 6) == 30);
10312     assert(maxDay(1999, 7) == 31);
10313     assert(maxDay(1999, 8) == 31);
10314     assert(maxDay(1999, 9) == 30);
10315     assert(maxDay(1999, 10) == 31);
10316     assert(maxDay(1999, 11) == 30);
10317     assert(maxDay(1999, 12) == 31);
10318 
10319     assert(maxDay(2000, 1) == 31);
10320     assert(maxDay(2000, 2) == 29);
10321     assert(maxDay(2000, 3) == 31);
10322     assert(maxDay(2000, 4) == 30);
10323     assert(maxDay(2000, 5) == 31);
10324     assert(maxDay(2000, 6) == 30);
10325     assert(maxDay(2000, 7) == 31);
10326     assert(maxDay(2000, 8) == 31);
10327     assert(maxDay(2000, 9) == 30);
10328     assert(maxDay(2000, 10) == 31);
10329     assert(maxDay(2000, 11) == 30);
10330     assert(maxDay(2000, 12) == 31);
10331 
10332     // Test B.C.
10333     assert(maxDay(-1999, 1) == 31);
10334     assert(maxDay(-1999, 2) == 28);
10335     assert(maxDay(-1999, 3) == 31);
10336     assert(maxDay(-1999, 4) == 30);
10337     assert(maxDay(-1999, 5) == 31);
10338     assert(maxDay(-1999, 6) == 30);
10339     assert(maxDay(-1999, 7) == 31);
10340     assert(maxDay(-1999, 8) == 31);
10341     assert(maxDay(-1999, 9) == 30);
10342     assert(maxDay(-1999, 10) == 31);
10343     assert(maxDay(-1999, 11) == 30);
10344     assert(maxDay(-1999, 12) == 31);
10345 
10346     assert(maxDay(-2000, 1) == 31);
10347     assert(maxDay(-2000, 2) == 29);
10348     assert(maxDay(-2000, 3) == 31);
10349     assert(maxDay(-2000, 4) == 30);
10350     assert(maxDay(-2000, 5) == 31);
10351     assert(maxDay(-2000, 6) == 30);
10352     assert(maxDay(-2000, 7) == 31);
10353     assert(maxDay(-2000, 8) == 31);
10354     assert(maxDay(-2000, 9) == 30);
10355     assert(maxDay(-2000, 10) == 31);
10356     assert(maxDay(-2000, 11) == 30);
10357     assert(maxDay(-2000, 12) == 31);
10358 }
10359 
10360 /+
10361     Splits out a particular unit from hnsecs and gives the value for that
10362     unit and the remaining hnsecs. It really shouldn't be used unless unless
10363     all units larger than the given units have already been split out.
10364 
10365     Params:
10366         units  = The units to split out.
10367         hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
10368                  after splitting out the given units.
10369 
10370     Returns:
10371         The number of the given units from converting hnsecs to those units.
10372   +/
10373 long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
10374 if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
10375 {
10376     import core.time : convert;
10377     immutable value = convert!("hnsecs", units)(hnsecs);
10378     hnsecs -= convert!(units, "hnsecs")(value);
10379     return value;
10380 }
10381 
10382 @safe unittest
10383 {
10384     auto hnsecs = 2595000000007L;
10385     immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
10386     assert(days == 3);
10387     assert(hnsecs == 3000000007);
10388 
10389     immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
10390     assert(minutes == 5);
10391     assert(hnsecs == 7);
10392 }
10393 
10394 
10395 /+
10396     Returns the day of the week for the given day of the Gregorian Calendar.
10397 
10398     Params:
10399         day = The day of the Gregorian Calendar for which to get the day of
10400               the week.
10401   +/
10402 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
10403 {
10404     // January 1st, 1 A.D. was a Monday
10405     if (day >= 0)
10406         return cast(DayOfWeek)(day % 7);
10407     else
10408     {
10409         immutable dow = cast(DayOfWeek)((day % 7) + 7);
10410 
10411         if (dow == 7)
10412             return DayOfWeek.sun;
10413         else
10414             return dow;
10415     }
10416 }
10417 
10418 @safe unittest
10419 {
10420     import std.datetime.systime : SysTime;
10421 
10422     // Test A.D.
10423     assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
10424     assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
10425     assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
10426     assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
10427     assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
10428     assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
10429     assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
10430     assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
10431     assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
10432     assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
10433     assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
10434     assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
10435     assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10436     assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10437     assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
10438     assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
10439     assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
10440     assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
10441     assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
10442     assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
10443     assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
10444     assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
10445 
10446     // Test B.C.
10447     assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
10448     assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
10449     assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
10450     assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
10451     assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
10452     assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
10453     assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
10454     assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
10455     assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
10456 }
10457 
10458 
10459 private:
10460 
10461 enum daysInYear     = 365;  // The number of days in a non-leap year.
10462 enum daysInLeapYear = 366;  // The numbef or days in a leap year.
10463 enum daysIn4Years   = daysInYear * 3 + daysInLeapYear;  // Number of days in 4 years.
10464 enum daysIn100Years = daysIn4Years * 25 - 1;  // The number of days in 100 years.
10465 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
10466 
10467 /+
10468     Array of integers representing the last days of each month in a year.
10469   +/
10470 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
10471 
10472 /+
10473     Array of integers representing the last days of each month in a leap year.
10474   +/
10475 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
10476 
10477 
10478 /+
10479     Returns the string representation of the given month.
10480   +/
10481 string monthToString(Month month) @safe pure
10482 {
10483     import std.format : format;
10484     assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
10485     return _monthNames[month - Month.jan];
10486 }
10487 
10488 @safe unittest
10489 {
10490     assert(monthToString(Month.jan) == "Jan");
10491     assert(monthToString(Month.feb) == "Feb");
10492     assert(monthToString(Month.mar) == "Mar");
10493     assert(monthToString(Month.apr) == "Apr");
10494     assert(monthToString(Month.may) == "May");
10495     assert(monthToString(Month.jun) == "Jun");
10496     assert(monthToString(Month.jul) == "Jul");
10497     assert(monthToString(Month.aug) == "Aug");
10498     assert(monthToString(Month.sep) == "Sep");
10499     assert(monthToString(Month.oct) == "Oct");
10500     assert(monthToString(Month.nov) == "Nov");
10501     assert(monthToString(Month.dec) == "Dec");
10502 }
10503 
10504 
10505 /+
10506     Returns the Month corresponding to the given string.
10507 
10508     Params:
10509         monthStr = The string representation of the month to get the Month for.
10510 
10511     Throws:
10512         $(REF DateTimeException,std,datetime,date) if the given month is not a
10513         valid month string.
10514   +/
10515 Month monthFromString(T)(T monthStr) @safe pure
10516 if (isSomeString!T)
10517 {
10518     import std.format : format;
10519     switch (monthStr)
10520     {
10521         case "Jan":
10522             return Month.jan;
10523         case "Feb":
10524             return Month.feb;
10525         case "Mar":
10526             return Month.mar;
10527         case "Apr":
10528             return Month.apr;
10529         case "May":
10530             return Month.may;
10531         case "Jun":
10532             return Month.jun;
10533         case "Jul":
10534             return Month.jul;
10535         case "Aug":
10536             return Month.aug;
10537         case "Sep":
10538             return Month.sep;
10539         case "Oct":
10540             return Month.oct;
10541         case "Nov":
10542             return Month.nov;
10543         case "Dec":
10544             return Month.dec;
10545         default:
10546             throw new DateTimeException(format!"Invalid month %s"(monthStr));
10547     }
10548 }
10549 
10550 @safe unittest
10551 {
10552     import std.conv : to;
10553     import std.traits : EnumMembers;
10554     foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
10555                       "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
10556     {
10557         assertThrown!DateTimeException(monthFromString(badStr), badStr);
10558     }
10559 
10560     foreach (month; EnumMembers!Month)
10561     {
10562         assert(monthFromString(monthToString(month)) == month, month.to!string);
10563     }
10564 }
10565 
10566 
10567 // NOTE: all the non-simple array literals are wrapped in functions, because
10568 // otherwise importing causes re-evaluation of the static initializers using
10569 // CTFE with unittests enabled
10570 version (StdUnittest)
10571 {
10572 private @safe:
10573     // All of these helper arrays are sorted in ascending order.
10574     auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
10575     auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
10576 
10577     // I'd use a Tuple, but I get forward reference errors if I try.
10578     struct MonthDay
10579     {
10580         Month month;
10581         short day;
10582 
10583         this(int m, short d)
10584         {
10585             month = cast(Month) m;
10586             day = d;
10587         }
10588     }
10589 
10590     MonthDay[] testMonthDays()
10591     {
10592         static MonthDay[] result = [MonthDay(1, 1),
10593                                 MonthDay(1, 2),
10594                                 MonthDay(3, 17),
10595                                 MonthDay(7, 4),
10596                                 MonthDay(10, 27),
10597                                 MonthDay(12, 30),
10598                                 MonthDay(12, 31)];
10599         return result;
10600     }
10601 
10602     auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
10603 
10604     TimeOfDay[] testTODs()
10605     {
10606         static result = [TimeOfDay(0, 0, 0),
10607                      TimeOfDay(0, 0, 1),
10608                      TimeOfDay(0, 1, 0),
10609                      TimeOfDay(1, 0, 0),
10610                      TimeOfDay(13, 13, 13),
10611                      TimeOfDay(23, 59, 59)];
10612         return result;
10613     }
10614 
10615     auto testHours = [0, 1, 12, 22, 23];
10616     auto testMinSecs = [0, 1, 30, 58, 59];
10617 
10618     // Throwing exceptions is incredibly expensive, so we want to use a smaller
10619     // set of values for tests using assertThrown.
10620     TimeOfDay[] testTODsThrown()
10621     {
10622        static result = [TimeOfDay(0, 0, 0),
10623                            TimeOfDay(13, 13, 13),
10624                            TimeOfDay(23, 59, 59)];
10625        return result;
10626     }
10627 
10628     Date[] testDatesBC;
10629     Date[] testDatesAD;
10630 
10631     DateTime[] testDateTimesBC;
10632     DateTime[] testDateTimesAD;
10633 
10634     // I'd use a Tuple, but I get forward reference errors if I try.
10635     struct GregDay { int day; Date date; }
10636     GregDay[] testGregDaysBC()
10637     {
10638        static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
10639                            GregDay(-735_233, Date(-2012, 1, 1)),
10640                            GregDay(-735_202, Date(-2012, 2, 1)),
10641                            GregDay(-735_175, Date(-2012, 2, 28)),
10642                            GregDay(-735_174, Date(-2012, 2, 29)),
10643                            GregDay(-735_173, Date(-2012, 3, 1)),
10644                            GregDay(-734_502, Date(-2010, 1, 1)),
10645                            GregDay(-734_472, Date(-2010, 1, 31)),
10646                            GregDay(-734_471, Date(-2010, 2, 1)),
10647                            GregDay(-734_444, Date(-2010, 2, 28)),
10648                            GregDay(-734_443, Date(-2010, 3, 1)),
10649                            GregDay(-734_413, Date(-2010, 3, 31)),
10650                            GregDay(-734_412, Date(-2010, 4, 1)),
10651                            GregDay(-734_383, Date(-2010, 4, 30)),
10652                            GregDay(-734_382, Date(-2010, 5, 1)),
10653                            GregDay(-734_352, Date(-2010, 5, 31)),
10654                            GregDay(-734_351, Date(-2010, 6, 1)),
10655                            GregDay(-734_322, Date(-2010, 6, 30)),
10656                            GregDay(-734_321, Date(-2010, 7, 1)),
10657                            GregDay(-734_291, Date(-2010, 7, 31)),
10658                            GregDay(-734_290, Date(-2010, 8, 1)),
10659                            GregDay(-734_260, Date(-2010, 8, 31)),
10660                            GregDay(-734_259, Date(-2010, 9, 1)),
10661                            GregDay(-734_230, Date(-2010, 9, 30)),
10662                            GregDay(-734_229, Date(-2010, 10, 1)),
10663                            GregDay(-734_199, Date(-2010, 10, 31)),
10664                            GregDay(-734_198, Date(-2010, 11, 1)),
10665                            GregDay(-734_169, Date(-2010, 11, 30)),
10666                            GregDay(-734_168, Date(-2010, 12, 1)),
10667                            GregDay(-734_139, Date(-2010, 12, 30)),
10668                            GregDay(-734_138, Date(-2010, 12, 31)),
10669                            GregDay(-731_215, Date(-2001, 1, 1)),
10670                            GregDay(-730_850, Date(-2000, 1, 1)),
10671                            GregDay(-730_849, Date(-2000, 1, 2)),
10672                            GregDay(-730_486, Date(-2000, 12, 30)),
10673                            GregDay(-730_485, Date(-2000, 12, 31)),
10674                            GregDay(-730_484, Date(-1999, 1, 1)),
10675                            GregDay(-694_690, Date(-1901, 1, 1)),
10676                            GregDay(-694_325, Date(-1900, 1, 1)),
10677                            GregDay(-585_118, Date(-1601, 1, 1)),
10678                            GregDay(-584_753, Date(-1600, 1, 1)),
10679                            GregDay(-584_388, Date(-1600, 12, 31)),
10680                            GregDay(-584_387, Date(-1599, 1, 1)),
10681                            GregDay(-365_972, Date(-1001, 1, 1)),
10682                            GregDay(-365_607, Date(-1000, 1, 1)),
10683                            GregDay(-183_351, Date(-501, 1, 1)),
10684                            GregDay(-182_986, Date(-500, 1, 1)),
10685                            GregDay(-182_621, Date(-499, 1, 1)),
10686                            GregDay(-146_827, Date(-401, 1, 1)),
10687                            GregDay(-146_462, Date(-400, 1, 1)),
10688                            GregDay(-146_097, Date(-400, 12, 31)),
10689                            GregDay(-110_302, Date(-301, 1, 1)),
10690                            GregDay(-109_937, Date(-300, 1, 1)),
10691                            GregDay(-73_778, Date(-201, 1, 1)),
10692                            GregDay(-73_413, Date(-200, 1, 1)),
10693                            GregDay(-38_715, Date(-105, 1, 1)),
10694                            GregDay(-37_254, Date(-101, 1, 1)),
10695                            GregDay(-36_889, Date(-100, 1, 1)),
10696                            GregDay(-36_524, Date(-99, 1, 1)),
10697                            GregDay(-36_160, Date(-99, 12, 31)),
10698                            GregDay(-35_794, Date(-97, 1, 1)),
10699                            GregDay(-18_627, Date(-50, 1, 1)),
10700                            GregDay(-18_262, Date(-49, 1, 1)),
10701                            GregDay(-3652, Date(-9, 1, 1)),
10702                            GregDay(-2191, Date(-5, 1, 1)),
10703                            GregDay(-1827, Date(-5, 12, 31)),
10704                            GregDay(-1826, Date(-4, 1, 1)),
10705                            GregDay(-1825, Date(-4, 1, 2)),
10706                            GregDay(-1462, Date(-4, 12, 30)),
10707                            GregDay(-1461, Date(-4, 12, 31)),
10708                            GregDay(-1460, Date(-3, 1, 1)),
10709                            GregDay(-1096, Date(-3, 12, 31)),
10710                            GregDay(-1095, Date(-2, 1, 1)),
10711                            GregDay(-731, Date(-2, 12, 31)),
10712                            GregDay(-730, Date(-1, 1, 1)),
10713                            GregDay(-367, Date(-1, 12, 30)),
10714                            GregDay(-366, Date(-1, 12, 31)),
10715                            GregDay(-365, Date(0, 1, 1)),
10716                            GregDay(-31, Date(0, 11, 30)),
10717                            GregDay(-30, Date(0, 12, 1)),
10718                            GregDay(-1, Date(0, 12, 30)),
10719                            GregDay(0, Date(0, 12, 31))];
10720        return result;
10721     }
10722 
10723     GregDay[] testGregDaysAD()
10724     {
10725        static result = [GregDay(1, Date(1, 1, 1)),
10726                            GregDay(2, Date(1, 1, 2)),
10727                            GregDay(32, Date(1, 2, 1)),
10728                            GregDay(365, Date(1, 12, 31)),
10729                            GregDay(366, Date(2, 1, 1)),
10730                            GregDay(731, Date(3, 1, 1)),
10731                            GregDay(1096, Date(4, 1, 1)),
10732                            GregDay(1097, Date(4, 1, 2)),
10733                            GregDay(1460, Date(4, 12, 30)),
10734                            GregDay(1461, Date(4, 12, 31)),
10735                            GregDay(1462, Date(5, 1, 1)),
10736                            GregDay(17_898, Date(50, 1, 1)),
10737                            GregDay(35_065, Date(97, 1, 1)),
10738                            GregDay(36_160, Date(100, 1, 1)),
10739                            GregDay(36_525, Date(101, 1, 1)),
10740                            GregDay(37_986, Date(105, 1, 1)),
10741                            GregDay(72_684, Date(200, 1, 1)),
10742                            GregDay(73_049, Date(201, 1, 1)),
10743                            GregDay(109_208, Date(300, 1, 1)),
10744                            GregDay(109_573, Date(301, 1, 1)),
10745                            GregDay(145_732, Date(400, 1, 1)),
10746                            GregDay(146_098, Date(401, 1, 1)),
10747                            GregDay(182_257, Date(500, 1, 1)),
10748                            GregDay(182_622, Date(501, 1, 1)),
10749                            GregDay(364_878, Date(1000, 1, 1)),
10750                            GregDay(365_243, Date(1001, 1, 1)),
10751                            GregDay(584_023, Date(1600, 1, 1)),
10752                            GregDay(584_389, Date(1601, 1, 1)),
10753                            GregDay(693_596, Date(1900, 1, 1)),
10754                            GregDay(693_961, Date(1901, 1, 1)),
10755                            GregDay(729_755, Date(1999, 1, 1)),
10756                            GregDay(730_120, Date(2000, 1, 1)),
10757                            GregDay(730_121, Date(2000, 1, 2)),
10758                            GregDay(730_484, Date(2000, 12, 30)),
10759                            GregDay(730_485, Date(2000, 12, 31)),
10760                            GregDay(730_486, Date(2001, 1, 1)),
10761                            GregDay(733_773, Date(2010, 1, 1)),
10762                            GregDay(733_774, Date(2010, 1, 2)),
10763                            GregDay(733_803, Date(2010, 1, 31)),
10764                            GregDay(733_804, Date(2010, 2, 1)),
10765                            GregDay(733_831, Date(2010, 2, 28)),
10766                            GregDay(733_832, Date(2010, 3, 1)),
10767                            GregDay(733_862, Date(2010, 3, 31)),
10768                            GregDay(733_863, Date(2010, 4, 1)),
10769                            GregDay(733_892, Date(2010, 4, 30)),
10770                            GregDay(733_893, Date(2010, 5, 1)),
10771                            GregDay(733_923, Date(2010, 5, 31)),
10772                            GregDay(733_924, Date(2010, 6, 1)),
10773                            GregDay(733_953, Date(2010, 6, 30)),
10774                            GregDay(733_954, Date(2010, 7, 1)),
10775                            GregDay(733_984, Date(2010, 7, 31)),
10776                            GregDay(733_985, Date(2010, 8, 1)),
10777                            GregDay(734_015, Date(2010, 8, 31)),
10778                            GregDay(734_016, Date(2010, 9, 1)),
10779                            GregDay(734_045, Date(2010, 9, 30)),
10780                            GregDay(734_046, Date(2010, 10, 1)),
10781                            GregDay(734_076, Date(2010, 10, 31)),
10782                            GregDay(734_077, Date(2010, 11, 1)),
10783                            GregDay(734_106, Date(2010, 11, 30)),
10784                            GregDay(734_107, Date(2010, 12, 1)),
10785                            GregDay(734_136, Date(2010, 12, 30)),
10786                            GregDay(734_137, Date(2010, 12, 31)),
10787                            GregDay(734_503, Date(2012, 1, 1)),
10788                            GregDay(734_534, Date(2012, 2, 1)),
10789                            GregDay(734_561, Date(2012, 2, 28)),
10790                            GregDay(734_562, Date(2012, 2, 29)),
10791                            GregDay(734_563, Date(2012, 3, 1)),
10792                            GregDay(734_858, Date(2012, 12, 21))];
10793        return result;
10794     }
10795 
10796     // I'd use a Tuple, but I get forward reference errors if I try.
10797     struct DayOfYear { int day; MonthDay md; }
10798     DayOfYear[] testDaysOfYear()
10799     {
10800        static result = [DayOfYear(1, MonthDay(1, 1)),
10801                            DayOfYear(2, MonthDay(1, 2)),
10802                            DayOfYear(3, MonthDay(1, 3)),
10803                            DayOfYear(31, MonthDay(1, 31)),
10804                            DayOfYear(32, MonthDay(2, 1)),
10805                            DayOfYear(59, MonthDay(2, 28)),
10806                            DayOfYear(60, MonthDay(3, 1)),
10807                            DayOfYear(90, MonthDay(3, 31)),
10808                            DayOfYear(91, MonthDay(4, 1)),
10809                            DayOfYear(120, MonthDay(4, 30)),
10810                            DayOfYear(121, MonthDay(5, 1)),
10811                            DayOfYear(151, MonthDay(5, 31)),
10812                            DayOfYear(152, MonthDay(6, 1)),
10813                            DayOfYear(181, MonthDay(6, 30)),
10814                            DayOfYear(182, MonthDay(7, 1)),
10815                            DayOfYear(212, MonthDay(7, 31)),
10816                            DayOfYear(213, MonthDay(8, 1)),
10817                            DayOfYear(243, MonthDay(8, 31)),
10818                            DayOfYear(244, MonthDay(9, 1)),
10819                            DayOfYear(273, MonthDay(9, 30)),
10820                            DayOfYear(274, MonthDay(10, 1)),
10821                            DayOfYear(304, MonthDay(10, 31)),
10822                            DayOfYear(305, MonthDay(11, 1)),
10823                            DayOfYear(334, MonthDay(11, 30)),
10824                            DayOfYear(335, MonthDay(12, 1)),
10825                            DayOfYear(363, MonthDay(12, 29)),
10826                            DayOfYear(364, MonthDay(12, 30)),
10827                            DayOfYear(365, MonthDay(12, 31))];
10828        return result;
10829     }
10830 
10831     DayOfYear[] testDaysOfLeapYear()
10832     {
10833        static result = [DayOfYear(1, MonthDay(1, 1)),
10834                                DayOfYear(2, MonthDay(1, 2)),
10835                                DayOfYear(3, MonthDay(1, 3)),
10836                                DayOfYear(31, MonthDay(1, 31)),
10837                                DayOfYear(32, MonthDay(2, 1)),
10838                                DayOfYear(59, MonthDay(2, 28)),
10839                                DayOfYear(60, MonthDay(2, 29)),
10840                                DayOfYear(61, MonthDay(3, 1)),
10841                                DayOfYear(91, MonthDay(3, 31)),
10842                                DayOfYear(92, MonthDay(4, 1)),
10843                                DayOfYear(121, MonthDay(4, 30)),
10844                                DayOfYear(122, MonthDay(5, 1)),
10845                                DayOfYear(152, MonthDay(5, 31)),
10846                                DayOfYear(153, MonthDay(6, 1)),
10847                                DayOfYear(182, MonthDay(6, 30)),
10848                                DayOfYear(183, MonthDay(7, 1)),
10849                                DayOfYear(213, MonthDay(7, 31)),
10850                                DayOfYear(214, MonthDay(8, 1)),
10851                                DayOfYear(244, MonthDay(8, 31)),
10852                                DayOfYear(245, MonthDay(9, 1)),
10853                                DayOfYear(274, MonthDay(9, 30)),
10854                                DayOfYear(275, MonthDay(10, 1)),
10855                                DayOfYear(305, MonthDay(10, 31)),
10856                                DayOfYear(306, MonthDay(11, 1)),
10857                                DayOfYear(335, MonthDay(11, 30)),
10858                                DayOfYear(336, MonthDay(12, 1)),
10859                                DayOfYear(364, MonthDay(12, 29)),
10860                                DayOfYear(365, MonthDay(12, 30)),
10861                                DayOfYear(366, MonthDay(12, 31))];
10862        return result;
10863     }
10864 
10865     void initializeTests()
10866     {
10867         foreach (year; testYearsBC)
10868         {
10869             foreach (md; testMonthDays)
10870                 testDatesBC ~= Date(year, md.month, md.day);
10871         }
10872 
10873         foreach (year; testYearsAD)
10874         {
10875             foreach (md; testMonthDays)
10876                 testDatesAD ~= Date(year, md.month, md.day);
10877         }
10878 
10879         foreach (dt; testDatesBC)
10880         {
10881             foreach (tod; testTODs)
10882                 testDateTimesBC ~= DateTime(dt, tod);
10883         }
10884 
10885         foreach (dt; testDatesAD)
10886         {
10887             foreach (tod; testTODs)
10888                 testDateTimesAD ~= DateTime(dt, tod);
10889         }
10890     }
10891 }