1 // Written in the D programming language
2 /++
3
4 $(SCRIPT inhibitQuickIndex = 1;)
5 $(DIVC quickindex,
6 $(BOOKTABLE,
7 $(TR $(TH Category) $(TH Functions))
8 $(TR $(TD Main date types) $(TD
9 $(LREF Date)
10 $(LREF DateTime)
11 ))
12 $(TR $(TD Other date types) $(TD
13 $(LREF Month)
14 $(LREF DayOfWeek)
15 $(LREF TimeOfDay)
16 ))
17 $(TR $(TD Date checking) $(TD
18 $(LREF valid)
19 $(LREF validTimeUnits)
20 $(LREF yearIsLeapYear)
21 $(LREF isTimePoint)
22 $(LREF enforceValid)
23 ))
24 $(TR $(TD Date conversion) $(TD
25 $(LREF daysToDayOfWeek)
26 $(LREF monthsToMonth)
27 ))
28 $(TR $(TD Time units) $(TD
29 $(LREF cmpTimeUnits)
30 $(LREF timeStrings)
31 ))
32 $(TR $(TD Other) $(TD
33 $(LREF AllowDayOverflow)
34 $(LREF DateTimeException)
35 ))
36 ))
37
38 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
39 Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
40 Source: $(PHOBOSSRC std/datetime/date.d)
41 +/
42 module std.datetime.date;
43
44 import core.time : TimeException;
45 import std.traits : isSomeString, Unqual;
46 import std.typecons : Flag;
47 import std.range.primitives : isOutputRange;
48
49 version (StdUnittest) import std.exception : assertThrown;
50
51 @safe unittest
52 {
53 initializeTests();
54 }
55
56
57 /++
58 Exception type used by std.datetime. It's an alias to
59 $(REF TimeException,core,time). Either can be caught without concern about
60 which module it came from.
61 +/
62 alias DateTimeException = TimeException;
63
64
65 /++
66 Represents the 12 months of the Gregorian year (January is 1).
67 +/
68 enum Month : ubyte
69 {
70 jan = 1, ///
71 feb, ///
72 mar, ///
73 apr, ///
74 may, ///
75 jun, ///
76 jul, ///
77 aug, ///
78 sep, ///
79 oct, ///
80 nov, ///
81 dec ///
82 }
83
84 ///
85 @safe pure unittest
86 {
87 assert(Date(2018, 10, 1).month == Month.oct);
88 assert(DateTime(1, 1, 1).month == Month.jan);
89 }
90
91
92 /++
93 Represents the 7 days of the Gregorian week (Sunday is 0).
94 +/
95 enum DayOfWeek : ubyte
96 {
97 sun = 0, ///
98 mon, ///
99 tue, ///
100 wed, ///
101 thu, ///
102 fri, ///
103 sat ///
104 }
105
106 ///
107 @safe pure unittest
108 {
109 assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
110 assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
111 }
112
113 /++
114 In some date calculations, adding months or years can cause the date to fall
115 on a day of the month which is not valid (e.g. February 29th 2001 or
116 June 31st 2000). If overflow is allowed (as is the default), then the month
117 will be incremented accordingly (so, February 29th 2001 would become
118 March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow
119 is not allowed, then the day will be adjusted to the last valid day in that
120 month (so, February 29th 2001 would become February 28th 2001 and
121 June 31st 2000 would become June 30th 2000).
122
123 AllowDayOverflow only applies to calculations involving months or years.
124
125 If set to `AllowDayOverflow.no`, then day overflow is not allowed.
126
127 Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
128 allowed.
129 +/
130 alias AllowDayOverflow = Flag!"allowDayOverflow";
131
132
133 /++
134 Array of the strings representing time units, starting with the smallest
135 unit and going to the largest. It does not include `"nsecs"`.
136
137 Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
138 `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
139 `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
140 `"years"`
141 +/
142 immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
143 "hours", "days", "weeks", "months", "years"];
144
145
146 /++
147 Combines the $(REF Date,std,datetime,date) and
148 $(REF TimeOfDay,std,datetime,date) structs to give an object which holds
149 both the date and the time. It is optimized for calendar-based operations
150 and has no concept of time zone. For an object which is optimized for time
151 operations based on the system time, use
152 $(REF SysTime,std,datetime,systime). $(REF SysTime,std,datetime,systime) has
153 a concept of time zone and has much higher precision (hnsecs). `DateTime`
154 is intended primarily for calendar-based uses rather than precise time
155 operations.
156 +/
157 struct DateTime
158 {
159 public:
160
161 /++
162 Params:
163 date = The date portion of $(LREF DateTime).
164 tod = The time portion of $(LREF DateTime).
165 +/
166 this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
167 {
168 _date = date;
169 _tod = tod;
170 }
171
172 @safe unittest
173 {
174 {
175 auto dt = DateTime.init;
176 assert(dt._date == Date.init);
177 assert(dt._tod == TimeOfDay.init);
178 }
179
180 {
181 auto dt = DateTime(Date(1999, 7 ,6));
182 assert(dt._date == Date(1999, 7, 6));
183 assert(dt._tod == TimeOfDay.init);
184 }
185
186 {
187 auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33));
188 assert(dt._date == Date(1999, 7, 6));
189 assert(dt._tod == TimeOfDay(12, 30, 33));
190 }
191 }
192
193
194 /++
195 Params:
196 year = The year portion of the date.
197 month = The month portion of the date (January is 1).
198 day = The day portion of the date.
199 hour = The hour portion of the time;
200 minute = The minute portion of the time;
201 second = The second portion of the time;
202 +/
203 this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure
204 {
205 _date = Date(year, month, day);
206 _tod = TimeOfDay(hour, minute, second);
207 }
208
209 @safe unittest
210 {
211 {
212 auto dt = DateTime(1999, 7 ,6);
213 assert(dt._date == Date(1999, 7, 6));
214 assert(dt._tod == TimeOfDay.init);
215 }
216
217 {
218 auto dt = DateTime(1999, 7 ,6, 12, 30, 33);
219 assert(dt._date == Date(1999, 7, 6));
220 assert(dt._tod == TimeOfDay(12, 30, 33));
221 }
222 }
223
224
225 /++
226 Compares this $(LREF DateTime) with the given `DateTime.`.
227
228 Returns:
229 $(BOOKTABLE,
230 $(TR $(TD this < rhs) $(TD < 0))
231 $(TR $(TD this == rhs) $(TD 0))
232 $(TR $(TD this > rhs) $(TD > 0))
233 )
234 +/
235 int opCmp(DateTime rhs) const @safe pure nothrow @nogc
236 {
237 immutable dateResult = _date.opCmp(rhs._date);
238
239 if (dateResult != 0)
240 return dateResult;
241
242 return _tod.opCmp(rhs._tod);
243 }
244
245 @safe unittest
246 {
247 // Test A.D.
248 assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0);
249
250 assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0);
251 assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0);
252 assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0);
253
254 assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0);
255 assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0);
256
257 assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0);
258
259 assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
260 assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
261 assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
262 assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
263 assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0);
264 assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0);
265
266 assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
267 assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
268 assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0);
269 assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
270 assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0);
271 assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0);
272
273
274 assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp(
275 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0);
276 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp(
277 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0);
278 assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp(
279 DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0);
280 assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
281 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
282
283 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp(
284 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0);
285 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
286 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
287
288 assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp(
289 DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0);
290 assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp(
291 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0);
292
293 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
294 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
295 assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
296 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
297 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
298 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
299 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
300 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
301 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
302 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0);
303 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
304 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
305
306 assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
307 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
308 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
309 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
310 assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
311 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
312 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
313 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0);
314
315 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
316 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
317 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
318 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0);
319
320 assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
321 DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
322 assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
323 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
324 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
325 DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
326 assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
327 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
328 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
329 DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0);
330 assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
331 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
332
333 assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
334 DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
335 assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
336 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
337 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
338 DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
339 assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
340 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0);
341 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
342 DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
343 assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
344 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
345
346 assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp(
347 DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
348 assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
349 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0);
350 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp(
351 DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0);
352 assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
353 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
354 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp(
355 DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
356 assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
357 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0);
358
359 // Test B.C.
360 assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp(
361 DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0);
362 assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
363 DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0);
364 assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp(
365 DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0);
366
367 assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp(
368 DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0);
369 assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
370 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0);
371
372 assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
373 DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0);
374
375 assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
376 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
377 assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
378 DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
379 assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
380 DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
381 assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
382 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
383 assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
384 DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
385 assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
386 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
387
388 assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
389 DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
390 assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
391 DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
392 assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
393 DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0);
394 assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
395 DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0);
396 assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
397 DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0);
398 assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
399 DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
400
401 // Test Both
402 assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
403 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
404 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
405 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
406
407 assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
408 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
409 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
410 DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0);
411
412 assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp(
413 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
414 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
415 DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0);
416
417 assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp(
418 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0);
419 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp(
420 DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0);
421
422 assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp(
423 DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0);
424 assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp(
425 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0);
426
427 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
428 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
429 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30));
430 assert(dt.opCmp(dt) == 0);
431 assert(dt.opCmp(cdt) == 0);
432 assert(dt.opCmp(idt) == 0);
433 assert(cdt.opCmp(dt) == 0);
434 assert(cdt.opCmp(cdt) == 0);
435 assert(cdt.opCmp(idt) == 0);
436 assert(idt.opCmp(dt) == 0);
437 assert(idt.opCmp(cdt) == 0);
438 assert(idt.opCmp(idt) == 0);
439 }
440
441
442 /++
443 The date portion of $(LREF DateTime).
444 +/
445 @property Date date() const @safe pure nothrow @nogc
446 {
447 return _date;
448 }
449
450 @safe unittest
451 {
452 {
453 auto dt = DateTime.init;
454 assert(dt.date == Date.init);
455 }
456
457 {
458 auto dt = DateTime(Date(1999, 7, 6));
459 assert(dt.date == Date(1999, 7, 6));
460 }
461
462 const cdt = DateTime(1999, 7, 6);
463 immutable idt = DateTime(1999, 7, 6);
464 assert(cdt.date == Date(1999, 7, 6));
465 assert(idt.date == Date(1999, 7, 6));
466 }
467
468
469 /++
470 The date portion of $(LREF DateTime).
471
472 Params:
473 date = The Date to set this $(LREF DateTime)'s date portion to.
474 +/
475 @property void date(Date date) @safe pure nothrow @nogc
476 {
477 _date = date;
478 }
479
480 @safe unittest
481 {
482 auto dt = DateTime.init;
483 dt.date = Date(1999, 7, 6);
484 assert(dt._date == Date(1999, 7, 6));
485 assert(dt._tod == TimeOfDay.init);
486
487 const cdt = DateTime(1999, 7, 6);
488 immutable idt = DateTime(1999, 7, 6);
489 static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1)));
490 static assert(!__traits(compiles, idt.date = Date(2010, 1, 1)));
491 }
492
493
494 /++
495 The time portion of $(LREF DateTime).
496 +/
497 @property TimeOfDay timeOfDay() const @safe pure nothrow @nogc
498 {
499 return _tod;
500 }
501
502 @safe unittest
503 {
504 {
505 auto dt = DateTime.init;
506 assert(dt.timeOfDay == TimeOfDay.init);
507 }
508
509 {
510 auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33));
511 assert(dt.timeOfDay == TimeOfDay(12, 30, 33));
512 }
513
514 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
515 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
516 assert(cdt.timeOfDay == TimeOfDay(12, 30, 33));
517 assert(idt.timeOfDay == TimeOfDay(12, 30, 33));
518 }
519
520
521 /++
522 The time portion of $(LREF DateTime).
523
524 Params:
525 tod = The $(REF TimeOfDay,std,datetime,date) to set this
526 $(LREF DateTime)'s time portion to.
527 +/
528 @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
529 {
530 _tod = tod;
531 }
532
533 @safe unittest
534 {
535 auto dt = DateTime.init;
536 dt.timeOfDay = TimeOfDay(12, 30, 33);
537 assert(dt._date == Date.init);
538 assert(dt._tod == TimeOfDay(12, 30, 33));
539
540 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
541 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
542 static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33)));
543 static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33)));
544 }
545
546
547 /++
548 Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
549 are B.C.
550 +/
551 @property short year() const @safe pure nothrow @nogc
552 {
553 return _date.year;
554 }
555
556 @safe unittest
557 {
558 assert(Date.init.year == 1);
559 assert(Date(1999, 7, 6).year == 1999);
560 assert(Date(-1999, 7, 6).year == -1999);
561
562 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
563 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
564 assert(idt.year == 1999);
565 assert(idt.year == 1999);
566 }
567
568
569 /++
570 Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
571 are B.C.
572
573 Params:
574 year = The year to set this $(LREF DateTime)'s year to.
575
576 Throws:
577 $(REF DateTimeException,std,datetime,date) if the new year is not
578 a leap year and if the resulting date would be on February 29th.
579 +/
580 @property void year(int year) @safe pure
581 {
582 _date.year = year;
583 }
584
585 ///
586 @safe unittest
587 {
588 assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
589 assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
590 assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
591 }
592
593 @safe unittest
594 {
595 static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
596 {
597 dt.year = year;
598 assert(dt == expected);
599 }
600
601 testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
602 1999,
603 DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)));
604 testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
605 0,
606 DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)));
607 testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
608 -1999,
609 DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)));
610
611 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
612 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
613 static assert(!__traits(compiles, cdt.year = 7));
614 static assert(!__traits(compiles, idt.year = 7));
615 }
616
617
618 /++
619 Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
620
621 Throws:
622 $(REF DateTimeException,std,datetime,date) if `isAD` is true.
623 +/
624 @property short yearBC() const @safe pure
625 {
626 return _date.yearBC;
627 }
628
629 ///
630 @safe unittest
631 {
632 assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
633 assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
634 assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
635 }
636
637 @safe unittest
638 {
639 assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
640
641 auto dt = DateTime(1999, 7, 6, 12, 30, 33);
642 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
643 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
644 dt.yearBC = 12;
645 assert(dt.yearBC == 12);
646 static assert(!__traits(compiles, cdt.yearBC = 12));
647 static assert(!__traits(compiles, idt.yearBC = 12));
648 }
649
650
651 /++
652 Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
653
654 Params:
655 year = The year B.C. to set this $(LREF DateTime)'s year to.
656
657 Throws:
658 $(REF DateTimeException,std,datetime,date) if a non-positive value
659 is given.
660 +/
661 @property void yearBC(int year) @safe pure
662 {
663 _date.yearBC = year;
664 }
665
666 ///
667 @safe unittest
668 {
669 auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
670 dt.yearBC = 1;
671 assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
672
673 dt.yearBC = 10;
674 assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
675 }
676
677 @safe unittest
678 {
679 assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1))));
680
681 auto dt = DateTime(1999, 7, 6, 12, 30, 33);
682 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
683 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
684 dt.yearBC = 12;
685 assert(dt.yearBC == 12);
686 static assert(!__traits(compiles, cdt.yearBC = 12));
687 static assert(!__traits(compiles, idt.yearBC = 12));
688 }
689
690
691 /++
692 Month of a Gregorian Year.
693 +/
694 @property Month month() const @safe pure nothrow @nogc
695 {
696 return _date.month;
697 }
698
699 ///
700 @safe unittest
701 {
702 assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
703 assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
704 assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
705 }
706
707 @safe unittest
708 {
709 assert(DateTime.init.month == 1);
710 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
711 assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7);
712
713 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
714 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
715 assert(cdt.month == 7);
716 assert(idt.month == 7);
717 }
718
719
720 /++
721 Month of a Gregorian Year.
722
723 Params:
724 month = The month to set this $(LREF DateTime)'s month to.
725
726 Throws:
727 $(REF DateTimeException,std,datetime,date) if the given month is
728 not a valid month.
729 +/
730 @property void month(Month month) @safe pure
731 {
732 _date.month = month;
733 }
734
735 @safe unittest
736 {
737 static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
738 {
739 dt.month = month;
740 assert(expected != DateTime.init);
741 assert(dt == expected);
742 }
743
744 assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0));
745 assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13));
746
747 testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)),
748 cast(Month) 7,
749 DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)));
750 testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)),
751 cast(Month) 7,
752 DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)));
753
754 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
755 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
756 static assert(!__traits(compiles, cdt.month = 12));
757 static assert(!__traits(compiles, idt.month = 12));
758 }
759
760
761 /++
762 Day of a Gregorian Month.
763 +/
764 @property ubyte day() const @safe pure nothrow @nogc
765 {
766 return _date.day;
767 }
768
769 ///
770 @safe unittest
771 {
772 assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
773 assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
774 assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
775 }
776
777 @safe unittest
778 {
779 import std.format : format;
780 import std.range : chain;
781
782 static void test(DateTime dateTime, int expected)
783 {
784 assert(dateTime.day == expected, format("Value given: %s", dateTime));
785 }
786
787 foreach (year; chain(testYearsBC, testYearsAD))
788 {
789 foreach (md; testMonthDays)
790 {
791 foreach (tod; testTODs)
792 test(DateTime(Date(year, md.month, md.day), tod), md.day);
793 }
794 }
795
796 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
797 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
798 assert(cdt.day == 6);
799 assert(idt.day == 6);
800 }
801
802
803 /++
804 Day of a Gregorian Month.
805
806 Params:
807 day = The day of the month to set this $(LREF DateTime)'s day to.
808
809 Throws:
810 $(REF DateTimeException,std,datetime,date) if the given day is not
811 a valid day of the current month.
812 +/
813 @property void day(int day) @safe pure
814 {
815 _date.day = day;
816 }
817
818 @safe unittest
819 {
820 import std.exception : assertNotThrown;
821
822 static void testDT(DateTime dt, int day)
823 {
824 dt.day = day;
825 }
826
827 // Test A.D.
828 assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0));
829 assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32));
830 assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29));
831 assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30));
832 assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32));
833 assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31));
834 assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32));
835 assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31));
836 assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32));
837 assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32));
838 assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31));
839 assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32));
840 assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31));
841 assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32));
842
843 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31));
844 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28));
845 assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29));
846 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31));
847 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30));
848 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31));
849 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30));
850 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31));
851 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31));
852 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30));
853 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31));
854 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30));
855 assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31));
856
857 {
858 auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22));
859 dt.day = 6;
860 assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22)));
861 }
862
863 // Test B.C.
864 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0));
865 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32));
866 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29));
867 assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30));
868 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32));
869 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31));
870 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32));
871 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31));
872 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32));
873 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32));
874 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31));
875 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32));
876 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31));
877 assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32));
878
879 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31));
880 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28));
881 assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29));
882 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31));
883 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30));
884 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31));
885 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30));
886 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31));
887 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31));
888 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30));
889 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31));
890 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30));
891 assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31));
892
893 auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22));
894 dt.day = 6;
895 assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22)));
896
897 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
898 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
899 static assert(!__traits(compiles, cdt.day = 27));
900 static assert(!__traits(compiles, idt.day = 27));
901 }
902
903
904 /++
905 Hours past midnight.
906 +/
907 @property ubyte hour() const @safe pure nothrow @nogc
908 {
909 return _tod.hour;
910 }
911
912 @safe unittest
913 {
914 assert(DateTime.init.hour == 0);
915 assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12);
916
917 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
918 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
919 assert(cdt.hour == 12);
920 assert(idt.hour == 12);
921 }
922
923
924 /++
925 Hours past midnight.
926
927 Params:
928 hour = The hour of the day to set this $(LREF DateTime)'s hour to.
929
930 Throws:
931 $(REF DateTimeException,std,datetime,date) if the given hour would
932 result in an invalid $(LREF DateTime).
933 +/
934 @property void hour(int hour) @safe pure
935 {
936 _tod.hour = hour;
937 }
938
939 @safe unittest
940 {
941 assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}());
942
943 auto dt = DateTime.init;
944 dt.hour = 12;
945 assert(dt == DateTime(1, 1, 1, 12, 0, 0));
946
947 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
948 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
949 static assert(!__traits(compiles, cdt.hour = 27));
950 static assert(!__traits(compiles, idt.hour = 27));
951 }
952
953
954 /++
955 Minutes past the hour.
956 +/
957 @property ubyte minute() const @safe pure nothrow @nogc
958 {
959 return _tod.minute;
960 }
961
962 @safe unittest
963 {
964 assert(DateTime.init.minute == 0);
965 assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30);
966
967 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
968 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
969 assert(cdt.minute == 30);
970 assert(idt.minute == 30);
971 }
972
973
974 /++
975 Minutes past the hour.
976
977 Params:
978 minute = The minute to set this $(LREF DateTime)'s minute to.
979
980 Throws:
981 $(REF DateTimeException,std,datetime,date) if the given minute
982 would result in an invalid $(LREF DateTime).
983 +/
984 @property void minute(int minute) @safe pure
985 {
986 _tod.minute = minute;
987 }
988
989 @safe unittest
990 {
991 assertThrown!DateTimeException((){DateTime.init.minute = 60;}());
992
993 auto dt = DateTime.init;
994 dt.minute = 30;
995 assert(dt == DateTime(1, 1, 1, 0, 30, 0));
996
997 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
998 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
999 static assert(!__traits(compiles, cdt.minute = 27));
1000 static assert(!__traits(compiles, idt.minute = 27));
1001 }
1002
1003
1004 /++
1005 Seconds past the minute.
1006 +/
1007 @property ubyte second() const @safe pure nothrow @nogc
1008 {
1009 return _tod.second;
1010 }
1011
1012 @safe unittest
1013 {
1014 assert(DateTime.init.second == 0);
1015 assert(DateTime(1, 1, 1, 0, 0, 33).second == 33);
1016
1017 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1018 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1019 assert(cdt.second == 33);
1020 assert(idt.second == 33);
1021 }
1022
1023
1024 /++
1025 Seconds past the minute.
1026
1027 Params:
1028 second = The second to set this $(LREF DateTime)'s second to.
1029
1030 Throws:
1031 $(REF DateTimeException,std,datetime,date) if the given seconds
1032 would result in an invalid $(LREF DateTime).
1033 +/
1034 @property void second(int second) @safe pure
1035 {
1036 _tod.second = second;
1037 }
1038
1039 @safe unittest
1040 {
1041 assertThrown!DateTimeException((){DateTime.init.second = 60;}());
1042
1043 auto dt = DateTime.init;
1044 dt.second = 33;
1045 assert(dt == DateTime(1, 1, 1, 0, 0, 33));
1046
1047 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1048 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1049 static assert(!__traits(compiles, cdt.second = 27));
1050 static assert(!__traits(compiles, idt.second = 27));
1051 }
1052
1053
1054 /++
1055 Adds the given number of years or months to this $(LREF DateTime),
1056 mutating it. A negative number will subtract.
1057
1058 Note that if day overflow is allowed, and the date with the adjusted
1059 year/month overflows the number of days in the new month, then the month
1060 will be incremented by one, and the day set to the number of days
1061 overflowed. (e.g. if the day were 31 and the new month were June, then
1062 the month would be incremented to July, and the new day would be 1). If
1063 day overflow is not allowed, then the day will be set to the last valid
1064 day in the month (e.g. June 31st would become June 30th).
1065
1066 Params:
1067 units = The type of units to add ("years" or "months").
1068 value = The number of months or years to add to this
1069 $(LREF DateTime).
1070 allowOverflow = Whether the days should be allowed to overflow,
1071 causing the month to increment.
1072
1073 Returns:
1074 A reference to the `DateTime` (`this`).
1075 +/
1076 ref DateTime add(string units)
1077 (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1078 if (units == "years" || units == "months")
1079 {
1080 _date.add!units(value, allowOverflow);
1081 return this;
1082 }
1083
1084 ///
1085 @safe unittest
1086 {
1087 auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
1088 dt1.add!"months"(11);
1089 assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
1090
1091 auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
1092 dt2.add!"months"(-11);
1093 assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
1094
1095 auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
1096 dt3.add!"years"(1);
1097 assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
1098
1099 auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
1100 dt4.add!"years"(1, AllowDayOverflow.no);
1101 assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
1102 }
1103
1104 @safe unittest
1105 {
1106 auto dt = DateTime(2000, 1, 31);
1107 dt.add!"years"(7).add!"months"(-4);
1108 assert(dt == DateTime(2006, 10, 1));
1109
1110 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1111 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1112 static assert(!__traits(compiles, cdt.add!"years"(4)));
1113 static assert(!__traits(compiles, idt.add!"years"(4)));
1114 static assert(!__traits(compiles, cdt.add!"months"(4)));
1115 static assert(!__traits(compiles, idt.add!"months"(4)));
1116 }
1117
1118
1119 /++
1120 Adds the given number of years or months to this $(LREF DateTime),
1121 mutating it. A negative number will subtract.
1122
1123 The difference between rolling and adding is that rolling does not
1124 affect larger units. Rolling a $(LREF DateTime) 12 months
1125 gets the exact same $(LREF DateTime). However, the days can still be
1126 affected due to the differing number of days in each month.
1127
1128 Because there are no units larger than years, there is no difference
1129 between adding and rolling years.
1130
1131 Params:
1132 units = The type of units to add ("years" or "months").
1133 value = The number of months or years to add to this
1134 $(LREF DateTime).
1135 allowOverflow = Whether the days should be allowed to overflow,
1136 causing the month to increment.
1137
1138 Returns:
1139 A reference to the `DateTime` (`this`).
1140 +/
1141 ref DateTime roll(string units)
1142 (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow @nogc
1143 if (units == "years" || units == "months")
1144 {
1145 _date.roll!units(value, allowOverflow);
1146 return this;
1147 }
1148
1149 ///
1150 @safe unittest
1151 {
1152 auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
1153 dt1.roll!"months"(1);
1154 assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
1155
1156 auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
1157 dt2.roll!"months"(-1);
1158 assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
1159
1160 auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
1161 dt3.roll!"months"(1);
1162 assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
1163
1164 auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
1165 dt4.roll!"months"(1, AllowDayOverflow.no);
1166 assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
1167
1168 auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
1169 dt5.roll!"years"(1);
1170 assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
1171
1172 auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
1173 dt6.roll!"years"(1, AllowDayOverflow.no);
1174 assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
1175 }
1176
1177 @safe unittest
1178 {
1179 auto dt = DateTime(2000, 1, 31);
1180 dt.roll!"years"(7).roll!"months"(-4);
1181 assert(dt == DateTime(2007, 10, 1));
1182
1183 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1184 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1185 static assert(!__traits(compiles, cdt.roll!"years"(4)));
1186 static assert(!__traits(compiles, idt.roll!"years"(4)));
1187 static assert(!__traits(compiles, cdt.roll!"months"(4)));
1188 static assert(!__traits(compiles, idt.roll!"months"(4)));
1189 }
1190
1191
1192 /++
1193 Adds the given number of units to this $(LREF DateTime), mutating it. A
1194 negative number will subtract.
1195
1196 The difference between rolling and adding is that rolling does not
1197 affect larger units. For instance, rolling a $(LREF DateTime) one
1198 year's worth of days gets the exact same $(LREF DateTime).
1199
1200 Accepted units are `"days"`, `"minutes"`, `"hours"`,
1201 `"minutes"`, and `"seconds"`.
1202
1203 Params:
1204 units = The units to add.
1205 value = The number of $(D_PARAM units) to add to this
1206 $(LREF DateTime).
1207
1208 Returns:
1209 A reference to the `DateTime` (`this`).
1210 +/
1211 ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1212 if (units == "days")
1213 {
1214 _date.roll!"days"(value);
1215 return this;
1216 }
1217
1218 ///
1219 @safe unittest
1220 {
1221 auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
1222 dt1.roll!"days"(1);
1223 assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
1224 dt1.roll!"days"(365);
1225 assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
1226 dt1.roll!"days"(-32);
1227 assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
1228
1229 auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
1230 dt2.roll!"hours"(1);
1231 assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
1232
1233 auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
1234 dt3.roll!"seconds"(-1);
1235 assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
1236 }
1237
1238 @safe unittest
1239 {
1240 auto dt = DateTime(2000, 1, 31);
1241 dt.roll!"days"(7).roll!"days"(-4);
1242 assert(dt == DateTime(2000, 1, 3));
1243
1244 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1245 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1246 static assert(!__traits(compiles, cdt.roll!"days"(4)));
1247 static assert(!__traits(compiles, idt.roll!"days"(4)));
1248 }
1249
1250
1251 /// ditto
1252 ref DateTime roll(string units)(long value) @safe pure nothrow @nogc
1253 if (units == "hours" ||
1254 units == "minutes" ||
1255 units == "seconds")
1256 {
1257 _tod.roll!units(value);
1258 return this;
1259 }
1260
1261 // Test roll!"hours"().
1262 @safe unittest
1263 {
1264 static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
1265 {
1266 orig.roll!"hours"(hours);
1267 assert(orig == expected);
1268 }
1269
1270 // Test A.D.
1271 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1272 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1273 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1274 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1275 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1276 DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1277 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1278 DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1279 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1280 DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1281 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1282 DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1283 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1284 DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1285 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1286 DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1287 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1288 DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1289 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1290 DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1291 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1292 DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1293 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1294 DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1295 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1296 DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1297 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1298 DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1299 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1300 DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1301 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1302 DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1303 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1304 DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1305 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1306 DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1307 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1308 DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1309 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1310 DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1311 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1312 DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1313 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1314 DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1315 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1316 DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1317 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1318 DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1319 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1320 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1321 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1322 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1323
1324 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1325 DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1326 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1327 DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33)));
1328 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1329 DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33)));
1330 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1331 DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33)));
1332 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1333 DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33)));
1334 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1335 DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33)));
1336 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1337 DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
1338 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1339 DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33)));
1340 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1341 DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33)));
1342 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1343 DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33)));
1344 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1345 DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1346 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1347 DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1348 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1349 DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1350 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1351 DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1352 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1353 DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33)));
1354 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1355 DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33)));
1356 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1357 DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
1358 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1359 DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33)));
1360 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1361 DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33)));
1362 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1363 DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33)));
1364 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1365 DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33)));
1366 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1367 DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33)));
1368 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1369 DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)));
1370 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1371 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1372 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1373 DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)));
1374
1375 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1376 DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33)));
1377 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1378 DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1379 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1380 DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1381
1382 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1383 DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)));
1384 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1385 DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)));
1386 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1387 DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33)));
1388
1389 testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1390 DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33)));
1391 testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1392 DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33)));
1393
1394 testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1,
1395 DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33)));
1396 testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1397 DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33)));
1398
1399 testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25,
1400 DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33)));
1401 testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25,
1402 DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33)));
1403
1404 testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1405 DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33)));
1406 testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1407 DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33)));
1408
1409 // Test B.C.
1410 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1411 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1412 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1413 DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1414 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1415 DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1416 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1417 DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1418 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1419 DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1420 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1421 DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1422 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6,
1423 DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1424 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7,
1425 DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1426 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8,
1427 DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1428 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9,
1429 DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1430 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1431 DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1432 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11,
1433 DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1434 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12,
1435 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1436 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13,
1437 DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1438 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14,
1439 DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1440 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1441 DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1442 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16,
1443 DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1444 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17,
1445 DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1446 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18,
1447 DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1448 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19,
1449 DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1450 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20,
1451 DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1452 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21,
1453 DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1454 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22,
1455 DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1456 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23,
1457 DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1458 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24,
1459 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1460 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25,
1461 DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1462
1463 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1464 DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1465 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1466 DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33)));
1467 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1468 DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33)));
1469 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1470 DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33)));
1471 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1472 DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33)));
1473 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6,
1474 DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33)));
1475 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7,
1476 DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33)));
1477 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8,
1478 DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33)));
1479 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9,
1480 DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33)));
1481 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1482 DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33)));
1483 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11,
1484 DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1485 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12,
1486 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1487 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13,
1488 DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1489 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14,
1490 DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1491 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1492 DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33)));
1493 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16,
1494 DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33)));
1495 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17,
1496 DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33)));
1497 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18,
1498 DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33)));
1499 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19,
1500 DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33)));
1501 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20,
1502 DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33)));
1503 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21,
1504 DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33)));
1505 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22,
1506 DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33)));
1507 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23,
1508 DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33)));
1509 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24,
1510 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1511 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25,
1512 DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33)));
1513
1514 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1,
1515 DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33)));
1516 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0,
1517 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1518 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1,
1519 DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1520
1521 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1,
1522 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)));
1523 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0,
1524 DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)));
1525 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1,
1526 DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33)));
1527
1528 testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1,
1529 DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33)));
1530 testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1,
1531 DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33)));
1532
1533 testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1,
1534 DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33)));
1535 testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1,
1536 DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33)));
1537
1538 testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25,
1539 DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33)));
1540 testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25,
1541 DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33)));
1542
1543 testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25,
1544 DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33)));
1545 testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25,
1546 DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33)));
1547
1548 // Test Both
1549 testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546,
1550 DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33)));
1551 testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546,
1552 DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33)));
1553
1554 auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1555 dt.roll!"hours"(27).roll!"hours"(-9);
1556 assert(dt == DateTime(2000, 1, 31, 3, 7, 6));
1557
1558 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1559 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1560 static assert(!__traits(compiles, cdt.roll!"hours"(4)));
1561 static assert(!__traits(compiles, idt.roll!"hours"(4)));
1562 }
1563
1564 // Test roll!"minutes"().
1565 @safe unittest
1566 {
1567 static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
1568 {
1569 orig.roll!"minutes"(minutes);
1570 assert(orig == expected);
1571 }
1572
1573 // Test A.D.
1574 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1575 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1576 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1577 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1578 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1579 DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33)));
1580 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1581 DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33)));
1582 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1583 DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33)));
1584 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1585 DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33)));
1586 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1587 DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33)));
1588 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1589 DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1590 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1591 DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1592 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1593 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1594 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1595 DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1596 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1597 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1598 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1599 DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1600 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1601 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1602 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1603 DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33)));
1604
1605 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1606 DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1607 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1608 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1609 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1610 DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1611 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1612 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1613 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1614 DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1615 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1616 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1617 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1618 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1619 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1620 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1621
1622 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1623 DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1624 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1625 DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33)));
1626 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1627 DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33)));
1628 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1629 DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33)));
1630 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1631 DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33)));
1632 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1633 DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33)));
1634 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1635 DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1636 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1637 DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1638 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1639 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1640 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1641 DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33)));
1642 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1643 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1644 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1645 DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33)));
1646 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1647 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1648 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1649 DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33)));
1650
1651 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1652 DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1653 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1654 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1655 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1656 DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1657 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1658 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1659 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1660 DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)));
1661 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1662 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1663 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1664 DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33)));
1665 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1666 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1667
1668 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1669 DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33)));
1670 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1671 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)));
1672 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1673 DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33)));
1674
1675 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1676 DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33)));
1677 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1678 DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)));
1679 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1680 DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33)));
1681
1682 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1683 DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33)));
1684 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1685 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)));
1686 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1687 DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33)));
1688
1689 testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1690 DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33)));
1691 testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1692 DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)));
1693 testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1694 DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33)));
1695
1696 testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1,
1697 DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33)));
1698 testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0,
1699 DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)));
1700 testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1,
1701 DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33)));
1702
1703 // Test B.C.
1704 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1705 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1706 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1707 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1708 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1709 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33)));
1710 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1711 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33)));
1712 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1713 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33)));
1714 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1715 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33)));
1716 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1717 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33)));
1718 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1719 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1720 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29,
1721 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1722 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1723 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1724 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45,
1725 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1726 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1727 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1728 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75,
1729 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1730 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90,
1731 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1732 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100,
1733 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33)));
1734
1735 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689,
1736 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1737 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690,
1738 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1739 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691,
1740 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1741 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960,
1742 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1743 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439,
1744 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1745 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440,
1746 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1747 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441,
1748 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1749 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880,
1750 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1751
1752 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1753 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1754 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1755 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33)));
1756 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1757 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33)));
1758 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1759 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33)));
1760 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1761 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33)));
1762 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1763 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33)));
1764 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1765 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1766 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29,
1767 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1768 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30,
1769 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1770 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45,
1771 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33)));
1772 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1773 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1774 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75,
1775 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33)));
1776 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90,
1777 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1778 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100,
1779 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33)));
1780
1781 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749,
1782 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1783 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750,
1784 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1785 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751,
1786 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1787 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960,
1788 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1789 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439,
1790 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33)));
1791 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440,
1792 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1793 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441,
1794 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33)));
1795 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880,
1796 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1797
1798 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1,
1799 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33)));
1800 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0,
1801 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)));
1802 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1,
1803 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33)));
1804
1805 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1,
1806 DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33)));
1807 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0,
1808 DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)));
1809 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1,
1810 DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33)));
1811
1812 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1,
1813 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33)));
1814 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0,
1815 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)));
1816 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1,
1817 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33)));
1818
1819 testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1,
1820 DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33)));
1821 testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0,
1822 DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)));
1823 testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1,
1824 DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33)));
1825
1826 testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1,
1827 DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33)));
1828 testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0,
1829 DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)));
1830 testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1,
1831 DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33)));
1832
1833 // Test Both
1834 testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
1835 DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0)));
1836 testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1,
1837 DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0)));
1838
1839 testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
1840 DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0)));
1841 testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1,
1842 DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0)));
1843
1844 testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760,
1845 DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
1846 testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760,
1847 DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1848
1849 testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782,
1850 DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33)));
1851 testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782,
1852 DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
1853
1854 auto dt = DateTime(2000, 1, 31, 9, 7, 6);
1855 dt.roll!"minutes"(92).roll!"minutes"(-292);
1856 assert(dt == DateTime(2000, 1, 31, 9, 47, 6));
1857
1858 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
1859 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
1860 static assert(!__traits(compiles, cdt.roll!"minutes"(4)));
1861 static assert(!__traits(compiles, idt.roll!"minutes"(4)));
1862 }
1863
1864 // Test roll!"seconds"().
1865 @safe unittest
1866 {
1867 static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
1868 {
1869 orig.roll!"seconds"(seconds);
1870 assert(orig == expected);
1871 }
1872
1873 // Test A.D.
1874 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1875 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1876 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1877 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1878 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1879 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35)));
1880 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1881 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36)));
1882 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1883 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37)));
1884 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1885 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38)));
1886 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1887 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43)));
1888 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1889 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48)));
1890 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
1891 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1892 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
1893 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1894 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
1895 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3)));
1896 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
1897 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1898 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
1899 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1900 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
1901 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1902
1903 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
1904 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1905 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
1906 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1907 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
1908 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1909 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
1910 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1911 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
1912 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1913 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
1914 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1915 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
1916 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1917 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
1918 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1919
1920 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
1921 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1922 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
1923 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31)));
1924 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
1925 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30)));
1926 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
1927 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29)));
1928 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
1929 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28)));
1930 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
1931 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23)));
1932 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
1933 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18)));
1934 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
1935 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1936 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
1937 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1938 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
1939 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58)));
1940 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
1941 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)));
1942 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
1943 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
1944 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
1945 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32)));
1946
1947 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
1948 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1)));
1949 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
1950 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)));
1951 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
1952 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59)));
1953
1954 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
1955 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1)));
1956 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
1957 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)));
1958 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
1959 DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59)));
1960
1961 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
1962 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1)));
1963 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
1964 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)));
1965 testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
1966 DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59)));
1967
1968 testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
1969 DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0)));
1970 testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
1971 DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)));
1972 testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
1973 DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58)));
1974
1975 testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1,
1976 DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0)));
1977 testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0,
1978 DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)));
1979 testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1,
1980 DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58)));
1981
1982 // Test B.C.
1983 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0,
1984 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
1985 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1,
1986 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
1987 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2,
1988 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35)));
1989 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3,
1990 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36)));
1991 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4,
1992 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37)));
1993 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5,
1994 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38)));
1995 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10,
1996 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43)));
1997 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15,
1998 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48)));
1999 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26,
2000 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2001 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27,
2002 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2003 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30,
2004 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3)));
2005 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59,
2006 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2007 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60,
2008 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2009 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61,
2010 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2011
2012 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766,
2013 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2014 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767,
2015 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2016 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768,
2017 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2018 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007,
2019 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2020 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599,
2021 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2022 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600,
2023 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2024 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601,
2025 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2026 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200,
2027 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2028
2029 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1,
2030 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2031 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2,
2032 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31)));
2033 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3,
2034 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30)));
2035 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4,
2036 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29)));
2037 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5,
2038 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28)));
2039 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10,
2040 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23)));
2041 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15,
2042 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18)));
2043 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33,
2044 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2045 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34,
2046 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2047 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35,
2048 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58)));
2049 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59,
2050 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34)));
2051 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60,
2052 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
2053 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61,
2054 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32)));
2055
2056 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1,
2057 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1)));
2058 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0,
2059 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)));
2060 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1,
2061 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59)));
2062
2063 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1,
2064 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1)));
2065 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0,
2066 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)));
2067 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1,
2068 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59)));
2069
2070 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1,
2071 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1)));
2072 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0,
2073 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)));
2074 testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1,
2075 DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59)));
2076
2077 testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1,
2078 DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0)));
2079 testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0,
2080 DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)));
2081 testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1,
2082 DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58)));
2083
2084 testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1,
2085 DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0)));
2086 testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0,
2087 DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)));
2088 testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1,
2089 DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58)));
2090
2091 // Test Both
2092 testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1,
2093 DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59)));
2094 testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1,
2095 DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)));
2096
2097 testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1,
2098 DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59)));
2099 testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1,
2100 DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)));
2101
2102 testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L,
2103 DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)));
2104 testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L,
2105 DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2106
2107 testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L,
2108 DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50)));
2109 testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L,
2110 DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)));
2111
2112 auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2113 dt.roll!"seconds"(92).roll!"seconds"(-292);
2114 assert(dt == DateTime(2000, 1, 31, 9, 7, 46));
2115
2116 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2117 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2118 static assert(!__traits(compiles, cdt.roll!"seconds"(4)));
2119 static assert(!__traits(compiles, idt.roll!"seconds"(4)));
2120 }
2121
2122
2123 import core.time : Duration;
2124 /++
2125 Gives the result of adding or subtracting a $(REF Duration, core,time)
2126 from this $(LREF DateTime).
2127
2128 The legal types of arithmetic for $(LREF DateTime) using this operator
2129 are
2130
2131 $(BOOKTABLE,
2132 $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
2133 $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
2134 )
2135
2136 Params:
2137 duration = The $(REF Duration, core,time) to add to or subtract from
2138 this $(LREF DateTime).
2139 +/
2140 DateTime opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
2141 if (op == "+" || op == "-")
2142 {
2143 DateTime retval = this;
2144 immutable seconds = duration.total!"seconds";
2145 mixin("return retval._addSeconds(" ~ op ~ "seconds);");
2146 }
2147
2148 ///
2149 @safe unittest
2150 {
2151 import core.time : hours, seconds;
2152
2153 assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
2154 DateTime(2016, 1, 1, 0, 0, 0));
2155
2156 assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
2157 DateTime(2016, 1, 1, 0, 59, 59));
2158
2159 assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
2160 DateTime(2015, 12, 31, 23, 59, 59));
2161
2162 assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
2163 DateTime(2015, 12, 31, 23, 59, 59));
2164 }
2165
2166 @safe unittest
2167 {
2168 import core.time : dur;
2169
2170 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2171
2172 assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2173 assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2174 assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2175 assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2176
2177 assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2178 assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2179 assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2180 assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2181 assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2182 assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2183 assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2184 assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2185 assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2186 assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2187 assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2188 assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2189
2190 assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2191 assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2192 assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2193 assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2194
2195 assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2196 assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2197 assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2198 assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2199 assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2200 assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2201 assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2202 assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2203 assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2204 assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2205 assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2206 assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2207
2208 auto duration = dur!"seconds"(12);
2209 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2210 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2211 assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2212 assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45));
2213 assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2214 assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21));
2215 }
2216
2217
2218 /++
2219 Gives the result of adding or subtracting a duration from this
2220 $(LREF DateTime), as well as assigning the result to this
2221 $(LREF DateTime).
2222
2223 The legal types of arithmetic for $(LREF DateTime) using this operator
2224 are
2225
2226 $(BOOKTABLE,
2227 $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime))
2228 $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime))
2229 )
2230
2231 Params:
2232 duration = The duration to add to or subtract from this
2233 $(LREF DateTime).
2234 +/
2235 ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
2236 if (op == "+" || op == "-")
2237 {
2238 import core.time : convert;
2239 import std.format : format;
2240
2241 DateTime retval = this;
2242 immutable hnsecs = duration.total!"hnsecs";
2243
2244 mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
2245 }
2246
2247 @safe unittest
2248 {
2249 import core.time : dur;
2250 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) ==
2251 DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2252 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) ==
2253 DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2254 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) ==
2255 DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2256 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) ==
2257 DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2258
2259 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) ==
2260 DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2261 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) ==
2262 DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2263 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) ==
2264 DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2265 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) ==
2266 DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2267 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) ==
2268 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2269 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) ==
2270 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2271 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) ==
2272 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2273 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) ==
2274 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2275 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) ==
2276 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2277 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) ==
2278 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2279 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) ==
2280 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2281 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) ==
2282 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2283
2284 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) ==
2285 DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33)));
2286 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) ==
2287 DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33)));
2288 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) ==
2289 DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33)));
2290 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) ==
2291 DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33)));
2292
2293 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) ==
2294 DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33)));
2295 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) ==
2296 DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33)));
2297 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) ==
2298 DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33)));
2299 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) ==
2300 DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33)));
2301 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) ==
2302 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2303 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) ==
2304 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2305 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) ==
2306 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2307 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) ==
2308 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2309 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) ==
2310 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2311 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) ==
2312 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2313 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) ==
2314 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40)));
2315 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) ==
2316 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26)));
2317
2318 auto dt = DateTime(2000, 1, 31, 9, 7, 6);
2319 (dt += dur!"seconds"(92)) -= dur!"days"(-500);
2320 assert(dt == DateTime(2001, 6, 14, 9, 8, 38));
2321
2322 auto duration = dur!"seconds"(12);
2323 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2324 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2325 static assert(!__traits(compiles, cdt += duration));
2326 static assert(!__traits(compiles, idt += duration));
2327 static assert(!__traits(compiles, cdt -= duration));
2328 static assert(!__traits(compiles, idt -= duration));
2329 }
2330
2331
2332 /++
2333 Gives the difference between two $(LREF DateTime)s.
2334
2335 The legal types of arithmetic for $(LREF DateTime) using this operator are
2336
2337 $(BOOKTABLE,
2338 $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration))
2339 )
2340 +/
2341 Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
2342 if (op == "-")
2343 {
2344 immutable dateResult = _date - rhs.date;
2345 immutable todResult = _tod - rhs._tod;
2346
2347 import core.time : dur;
2348 return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs");
2349 }
2350
2351 @safe unittest
2352 {
2353 auto dt = DateTime(1999, 7, 6, 12, 30, 33);
2354
2355 import core.time : dur;
2356 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) ==
2357 dur!"seconds"(31_536_000));
2358 assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2359 dur!"seconds"(-31_536_000));
2360
2361 assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2362 dur!"seconds"(26_78_400));
2363 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) ==
2364 dur!"seconds"(-26_78_400));
2365
2366 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) ==
2367 dur!"seconds"(86_400));
2368 assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2369 dur!"seconds"(-86_400));
2370
2371 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) ==
2372 dur!"seconds"(3600));
2373 assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2374 dur!"seconds"(-3600));
2375
2376 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2377 dur!"seconds"(60));
2378 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) ==
2379 dur!"seconds"(-60));
2380
2381 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) ==
2382 dur!"seconds"(1));
2383 assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) ==
2384 dur!"seconds"(-1));
2385
2386 assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033));
2387 assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033));
2388 assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367));
2389 assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367));
2390
2391 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2392 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2393 assert(dt - dt == Duration.zero);
2394 assert(cdt - dt == Duration.zero);
2395 assert(idt - dt == Duration.zero);
2396
2397 assert(dt - cdt == Duration.zero);
2398 assert(cdt - cdt == Duration.zero);
2399 assert(idt - cdt == Duration.zero);
2400
2401 assert(dt - idt == Duration.zero);
2402 assert(cdt - idt == Duration.zero);
2403 assert(idt - idt == Duration.zero);
2404 }
2405
2406
2407 /++
2408 Returns the difference between the two $(LREF DateTime)s in months.
2409
2410 To get the difference in years, subtract the year property
2411 of two $(LREF DateTime)s. To get the difference in days or weeks,
2412 subtract the $(LREF DateTime)s themselves and use the
2413 $(REF Duration, core,time) that results. Because converting between
2414 months and smaller units requires a specific date (which
2415 $(REF Duration, core,time)s don't have), getting the difference in
2416 months requires some math using both the year and month properties, so
2417 this is a convenience function for getting the difference in months.
2418
2419 Note that the number of days in the months or how far into the month
2420 either date is is irrelevant. It is the difference in the month property
2421 combined with the difference in years * 12. So, for instance,
2422 December 31st and January 1st are one month apart just as December 1st
2423 and January 31st are one month apart.
2424
2425 Params:
2426 rhs = The $(LREF DateTime) to subtract from this one.
2427 +/
2428 int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
2429 {
2430 return _date.diffMonths(rhs._date);
2431 }
2432
2433 ///
2434 @safe unittest
2435 {
2436 assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
2437 DateTime(1999, 1, 31, 23, 59, 59)) == 1);
2438
2439 assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
2440 DateTime(1999, 2, 1, 12, 3, 42)) == -1);
2441
2442 assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
2443 DateTime(1999, 1, 1, 2, 4, 7)) == 2);
2444
2445 assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
2446 DateTime(1999, 3, 31, 0, 30, 58)) == -2);
2447 }
2448
2449 @safe unittest
2450 {
2451 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2452 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2453 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2454 assert(dt.diffMonths(dt) == 0);
2455 assert(cdt.diffMonths(dt) == 0);
2456 assert(idt.diffMonths(dt) == 0);
2457
2458 assert(dt.diffMonths(cdt) == 0);
2459 assert(cdt.diffMonths(cdt) == 0);
2460 assert(idt.diffMonths(cdt) == 0);
2461
2462 assert(dt.diffMonths(idt) == 0);
2463 assert(cdt.diffMonths(idt) == 0);
2464 assert(idt.diffMonths(idt) == 0);
2465 }
2466
2467
2468 /++
2469 Whether this $(LREF DateTime) is in a leap year.
2470 +/
2471 @property bool isLeapYear() const @safe pure nothrow @nogc
2472 {
2473 return _date.isLeapYear;
2474 }
2475
2476 @safe unittest
2477 {
2478 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2479 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2480 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2481 assert(!dt.isLeapYear);
2482 assert(!cdt.isLeapYear);
2483 assert(!idt.isLeapYear);
2484 }
2485
2486
2487 /++
2488 Day of the week this $(LREF DateTime) is on.
2489 +/
2490 @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
2491 {
2492 return _date.dayOfWeek;
2493 }
2494
2495 @safe unittest
2496 {
2497 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2498 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2499 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2500 assert(dt.dayOfWeek == DayOfWeek.tue);
2501 assert(cdt.dayOfWeek == DayOfWeek.tue);
2502 assert(idt.dayOfWeek == DayOfWeek.tue);
2503 }
2504
2505
2506 /++
2507 Day of the year this $(LREF DateTime) is on.
2508 +/
2509 @property ushort dayOfYear() const @safe pure nothrow @nogc
2510 {
2511 return _date.dayOfYear;
2512 }
2513
2514 ///
2515 @safe unittest
2516 {
2517 assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
2518 assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
2519 assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
2520 }
2521
2522 @safe unittest
2523 {
2524 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2525 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2526 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2527 assert(dt.dayOfYear == 187);
2528 assert(cdt.dayOfYear == 187);
2529 assert(idt.dayOfYear == 187);
2530 }
2531
2532
2533 /++
2534 Day of the year.
2535
2536 Params:
2537 day = The day of the year to set which day of the year this
2538 $(LREF DateTime) is on.
2539 +/
2540 @property void dayOfYear(int day) @safe pure
2541 {
2542 _date.dayOfYear = day;
2543 }
2544
2545 @safe unittest
2546 {
2547 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2548 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2549 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2550 dt.dayOfYear = 12;
2551 assert(dt.dayOfYear == 12);
2552 static assert(!__traits(compiles, cdt.dayOfYear = 12));
2553 static assert(!__traits(compiles, idt.dayOfYear = 12));
2554 }
2555
2556
2557 /++
2558 The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2559 +/
2560 @property int dayOfGregorianCal() const @safe pure nothrow @nogc
2561 {
2562 return _date.dayOfGregorianCal;
2563 }
2564
2565 ///
2566 @safe unittest
2567 {
2568 assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
2569 assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
2570 assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
2571
2572 assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
2573 assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
2574 assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
2575
2576 assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
2577 assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
2578 }
2579
2580 @safe unittest
2581 {
2582 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2583 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2584 assert(cdt.dayOfGregorianCal == 729_941);
2585 assert(idt.dayOfGregorianCal == 729_941);
2586 }
2587
2588
2589 /++
2590 The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on.
2591 Setting this property does not affect the time portion of
2592 $(LREF DateTime).
2593
2594 Params:
2595 days = The day of the Gregorian Calendar to set this $(LREF DateTime)
2596 to.
2597 +/
2598 @property void dayOfGregorianCal(int days) @safe pure nothrow @nogc
2599 {
2600 _date.dayOfGregorianCal = days;
2601 }
2602
2603 ///
2604 @safe unittest
2605 {
2606 auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
2607 dt.dayOfGregorianCal = 1;
2608 assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
2609
2610 dt.dayOfGregorianCal = 365;
2611 assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
2612
2613 dt.dayOfGregorianCal = 366;
2614 assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
2615
2616 dt.dayOfGregorianCal = 0;
2617 assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
2618
2619 dt.dayOfGregorianCal = -365;
2620 assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
2621
2622 dt.dayOfGregorianCal = -366;
2623 assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
2624
2625 dt.dayOfGregorianCal = 730_120;
2626 assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
2627
2628 dt.dayOfGregorianCal = 734_137;
2629 assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
2630 }
2631
2632 @safe unittest
2633 {
2634 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2635 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2636 static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7));
2637 static assert(!__traits(compiles, idt.dayOfGregorianCal = 7));
2638 }
2639
2640
2641 /++
2642 The ISO 8601 week of the year that this $(LREF DateTime) is in.
2643
2644 See_Also:
2645 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2646 +/
2647 @property ubyte isoWeek() const @safe pure nothrow
2648 {
2649 return _date.isoWeek;
2650 }
2651
2652 @safe unittest
2653 {
2654 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2655 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2656 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2657 assert(dt.isoWeek == 27);
2658 assert(cdt.isoWeek == 27);
2659 assert(idt.isoWeek == 27);
2660 }
2661
2662
2663 /++
2664 The year of the ISO 8601 week calendar that this $(LREF DateTime) is in.
2665
2666 See_Also:
2667 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
2668 +/
2669 @property short isoWeekYear() const @safe pure nothrow
2670 {
2671 return _date.isoWeekYear;
2672 }
2673
2674
2675 /++
2676 $(LREF DateTime) for the last day in the month that this
2677 $(LREF DateTime) is in. The time portion of endOfMonth is always
2678 23:59:59.
2679 +/
2680 @property DateTime endOfMonth() const @safe pure nothrow
2681 {
2682 try
2683 return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59));
2684 catch (Exception e)
2685 assert(0, "DateTime constructor threw.");
2686 }
2687
2688 ///
2689 @safe unittest
2690 {
2691 assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
2692 DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
2693
2694 assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
2695 DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
2696
2697 assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
2698 DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
2699
2700 assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
2701 DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
2702 }
2703
2704 @safe unittest
2705 {
2706 // Test A.D.
2707 assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59));
2708 assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59));
2709 assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59));
2710 assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59));
2711 assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59));
2712 assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59));
2713 assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59));
2714 assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2715 assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59));
2716 assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59));
2717 assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59));
2718 assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59));
2719 assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59));
2720
2721 // Test B.C.
2722 assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59));
2723 assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59));
2724 assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59));
2725 assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59));
2726 assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59));
2727 assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59));
2728 assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59));
2729 assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59));
2730 assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59));
2731 assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59));
2732 assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59));
2733 assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59));
2734 assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59));
2735
2736 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2737 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2738 assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2739 assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59));
2740 }
2741
2742
2743 /++
2744 The last day in the month that this $(LREF DateTime) is in.
2745 +/
2746 @property ubyte daysInMonth() const @safe pure nothrow @nogc
2747 {
2748 return _date.daysInMonth;
2749 }
2750
2751 ///
2752 @safe unittest
2753 {
2754 assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
2755 assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
2756 assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
2757 assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
2758 }
2759
2760 @safe unittest
2761 {
2762 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2763 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2764 assert(cdt.daysInMonth == 31);
2765 assert(idt.daysInMonth == 31);
2766 }
2767
2768
2769 /++
2770 Whether the current year is a date in A.D.
2771 +/
2772 @property bool isAD() const @safe pure nothrow @nogc
2773 {
2774 return _date.isAD;
2775 }
2776
2777 ///
2778 @safe unittest
2779 {
2780 assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
2781 assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
2782 assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
2783 assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
2784 }
2785
2786 @safe unittest
2787 {
2788 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2789 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2790 assert(cdt.isAD);
2791 assert(idt.isAD);
2792 }
2793
2794
2795 /++
2796 The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
2797 $(LREF DateTime) at the given time. For example, prior to noon,
2798 1996-03-31 would be the Julian day number 2_450_173, so this function
2799 returns 2_450_173, while from noon onward, the julian day number would
2800 be 2_450_174, so this function returns 2_450_174.
2801 +/
2802 @property long julianDay() const @safe pure nothrow @nogc
2803 {
2804 if (_tod._hour < 12)
2805 return _date.julianDay - 1;
2806 else
2807 return _date.julianDay;
2808 }
2809
2810 @safe unittest
2811 {
2812 assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1);
2813 assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0);
2814
2815 assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424);
2816 assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425);
2817
2818 assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425);
2819 assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426);
2820
2821 assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160);
2822 assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161);
2823
2824 assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000);
2825 assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001);
2826
2827 assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973);
2828 assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974);
2829
2830 assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173);
2831 assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174);
2832
2833 assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432);
2834 assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433);
2835
2836 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2837 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2838 assert(cdt.julianDay == 2_451_366);
2839 assert(idt.julianDay == 2_451_366);
2840 }
2841
2842
2843 /++
2844 The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any
2845 time on this date (since, the modified Julian day changes at midnight).
2846 +/
2847 @property long modJulianDay() const @safe pure nothrow @nogc
2848 {
2849 return _date.modJulianDay;
2850 }
2851
2852 @safe unittest
2853 {
2854 assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0);
2855 assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0);
2856
2857 assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432);
2858 assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432);
2859
2860 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2861 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
2862 assert(cdt.modJulianDay == 51_365);
2863 assert(idt.modJulianDay == 51_365);
2864 }
2865
2866
2867 /++
2868 Converts this $(LREF DateTime) to a string with the format `YYYYMMDDTHHMMSS`.
2869 If `writer` is set, the resulting string will be written directly to it.
2870
2871 Params:
2872 writer = A `char` accepting
2873 $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2874 Returns:
2875 A `string` when not using an output range; `void` otherwise.
2876 +/
2877 string toISOString() const @safe pure nothrow
2878 {
2879 import std.array : appender;
2880 auto w = appender!string();
2881 w.reserve(18);
2882 try
2883 toISOString(w);
2884 catch (Exception e)
2885 assert(0, "toISOString() threw.");
2886 return w.data;
2887 }
2888
2889 /// ditto
2890 void toISOString(W)(ref W writer) const
2891 if (isOutputRange!(W, char))
2892 {
2893 import std.format.write : formattedWrite;
2894 _date.toISOString(writer);
2895 formattedWrite!("T%02d%02d%02d")(
2896 writer,
2897 _tod._hour,
2898 _tod._minute,
2899 _tod._second
2900 );
2901 }
2902
2903 ///
2904 @safe unittest
2905 {
2906 assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
2907 "20100704T070612");
2908
2909 assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
2910 "19981225T021500");
2911
2912 assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
2913 "00000105T230959");
2914
2915 assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
2916 "-00040105T000002");
2917 }
2918
2919 @safe unittest
2920 {
2921 // Test A.D.
2922 assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000");
2923 assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612");
2924 assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459");
2925 assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959");
2926 assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101");
2927
2928 // Test B.C.
2929 assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204");
2930 assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000");
2931 assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612");
2932 assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459");
2933 assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959");
2934 assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101");
2935
2936 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
2937 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
2938 assert(cdt.toISOString() == "19990706T123033");
2939 assert(idt.toISOString() == "19990706T123033");
2940 }
2941
2942
2943 /++
2944 Converts this $(LREF DateTime) to a string with the format
2945 `YYYY-MM-DDTHH:MM:SS`. If `writer` is set, the resulting
2946 string will be written directly to it.
2947
2948 Params:
2949 writer = A `char` accepting
2950 $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
2951 Returns:
2952 A `string` when not using an output range; `void` otherwise.
2953 +/
2954 string toISOExtString() const @safe pure nothrow
2955 {
2956 import std.array : appender;
2957 auto w = appender!string();
2958 w.reserve(20);
2959 try
2960 toISOExtString(w);
2961 catch (Exception e)
2962 assert(0, "toISOExtString() threw.");
2963 return w.data;
2964 }
2965
2966 /// ditto
2967 void toISOExtString(W)(ref W writer) const
2968 if (isOutputRange!(W, char))
2969 {
2970 import std.format.write : formattedWrite;
2971 _date.toISOExtString(writer);
2972 formattedWrite!("T%02d:%02d:%02d")(
2973 writer,
2974 _tod._hour,
2975 _tod._minute,
2976 _tod._second
2977 );
2978 }
2979
2980 ///
2981 @safe unittest
2982 {
2983 assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
2984 "2010-07-04T07:06:12");
2985
2986 assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
2987 "1998-12-25T02:15:00");
2988
2989 assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
2990 "0000-01-05T23:09:59");
2991
2992 assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
2993 "-0004-01-05T00:00:02");
2994 }
2995
2996 @safe unittest
2997 {
2998 // Test A.D.
2999 assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00");
3000 assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12");
3001 assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59");
3002 assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59");
3003 assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01");
3004
3005 // Test B.C.
3006 assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04");
3007 assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00");
3008 assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12");
3009 assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59");
3010 assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59");
3011 assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01");
3012
3013 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3014 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3015 assert(cdt.toISOExtString() == "1999-07-06T12:30:33");
3016 assert(idt.toISOExtString() == "1999-07-06T12:30:33");
3017 }
3018
3019 /++
3020 Converts this $(LREF DateTime) to a string with the format
3021 `YYYY-Mon-DD HH:MM:SS`. If `writer` is set, the resulting
3022 string will be written directly to it.
3023
3024 Params:
3025 writer = A `char` accepting
3026 $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3027 Returns:
3028 A `string` when not using an output range; `void` otherwise.
3029 +/
3030 string toSimpleString() const @safe pure nothrow
3031 {
3032 import std.array : appender;
3033 auto w = appender!string();
3034 w.reserve(22);
3035 try
3036 toSimpleString(w);
3037 catch (Exception e)
3038 assert(0, "toSimpleString() threw.");
3039 return w.data;
3040 }
3041
3042 /// ditto
3043 void toSimpleString(W)(ref W writer) const
3044 if (isOutputRange!(W, char))
3045 {
3046 import std.format.write : formattedWrite;
3047 _date.toSimpleString(writer);
3048 formattedWrite!(" %02d:%02d:%02d")(
3049 writer,
3050 _tod._hour,
3051 _tod._minute,
3052 _tod._second
3053 );
3054 }
3055
3056 ///
3057 @safe unittest
3058 {
3059 assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
3060 "2010-Jul-04 07:06:12");
3061
3062 assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
3063 "1998-Dec-25 02:15:00");
3064
3065 assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
3066 "0000-Jan-05 23:09:59");
3067
3068 assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
3069 "-0004-Jan-05 00:00:02");
3070 }
3071
3072 @safe unittest
3073 {
3074 // Test A.D.
3075 assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00");
3076 assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12");
3077 assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59");
3078 assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59");
3079 assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01");
3080
3081 // Test B.C.
3082 assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04");
3083 assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00");
3084 assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12");
3085 assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59");
3086 assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59");
3087 assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01");
3088
3089 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3090 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3091 assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33");
3092 assert(idt.toSimpleString() == "1999-Jul-06 12:30:33");
3093 }
3094
3095
3096 /++
3097 Converts this $(LREF DateTime) to a string.
3098
3099 This function exists to make it easy to convert a $(LREF DateTime) to a
3100 string for code that does not care what the exact format is - just that
3101 it presents the information in a clear manner. It also makes it easy to
3102 simply convert a $(LREF DateTime) to a string when using functions such
3103 as `to!string`, `format`, or `writeln` which use toString to convert
3104 user-defined types. So, it is unlikely that much code will call
3105 toString directly.
3106
3107 The format of the string is purposefully unspecified, and code that
3108 cares about the format of the string should use `toISOString`,
3109 `toISOExtString`, `toSimpleString`, or some other custom formatting
3110 function that explicitly generates the format that the code needs. The
3111 reason is that the code is then clear about what format it's using,
3112 making it less error-prone to maintain the code and interact with other
3113 software that consumes the generated strings. It's for this same reason
3114 that $(LREF DateTime) has no `fromString` function, whereas it does have
3115 `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
3116
3117 The format returned by toString may or may not change in the future.
3118 +/
3119 string toString() const @safe pure nothrow
3120 {
3121 return toSimpleString();
3122 }
3123
3124 @safe unittest
3125 {
3126 auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3127 const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3128 immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33));
3129 assert(dt.toString());
3130 assert(cdt.toString());
3131 assert(idt.toString());
3132 }
3133
3134 /// ditto
3135 void toString(W)(ref W writer) const
3136 if (isOutputRange!(W, char))
3137 {
3138 toSimpleString(writer);
3139 }
3140
3141 /++
3142 Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS.
3143 Whitespace is stripped from the given string.
3144
3145 Params:
3146 isoString = A string formatted in the ISO format for dates and times.
3147
3148 Throws:
3149 $(REF DateTimeException,std,datetime,date) if the given string is
3150 not in the ISO format or if the resulting $(LREF DateTime) would not
3151 be valid.
3152 +/
3153 static DateTime fromISOString(S)(scope const S isoString) @safe pure
3154 if (isSomeString!S)
3155 {
3156 import std.algorithm.searching : countUntil;
3157 import std.exception : enforce;
3158 import std.format : format;
3159 import std.string : strip;
3160 import std.utf : byCodeUnit;
3161
3162 auto str = strip(isoString);
3163
3164 enforce!DateTimeException(str.length >= 15, format("Invalid format for DateTime.fromISOString %s", isoString));
3165 auto t = str.byCodeUnit.countUntil('T');
3166
3167 enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromISOString: %s", isoString));
3168
3169 immutable date = Date.fromISOString(str[0 .. t]);
3170 immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
3171
3172 return DateTime(date, tod);
3173 }
3174
3175 ///
3176 @safe unittest
3177 {
3178 assert(DateTime.fromISOString("20100704T070612") ==
3179 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3180
3181 assert(DateTime.fromISOString("19981225T021500") ==
3182 DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3183
3184 assert(DateTime.fromISOString("00000105T230959") ==
3185 DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3186
3187 assert(DateTime.fromISOString("-00040105T000002") ==
3188 DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3189
3190 assert(DateTime.fromISOString(" 20100704T070612 ") ==
3191 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3192 }
3193
3194 @safe unittest
3195 {
3196 assertThrown!DateTimeException(DateTime.fromISOString(""));
3197 assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3198 assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3199 assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3200 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3201 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3202
3203 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3204 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3205 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3206 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3207 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3208
3209 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3210 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3211 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3212 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3213 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3214 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3215
3216 assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
3217 assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
3218
3219 assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3220 assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3221 assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3222 assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3223 assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3224 assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3225 assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3226 }
3227
3228 // https://issues.dlang.org/show_bug.cgi?id=17801
3229 @safe unittest
3230 {
3231 import std.conv : to;
3232 import std.meta : AliasSeq;
3233 static foreach (C; AliasSeq!(char, wchar, dchar))
3234 {
3235 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3236 assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
3237 }
3238 }
3239
3240
3241 /++
3242 Creates a $(LREF DateTime) from a string with the format
3243 YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string.
3244
3245 Params:
3246 isoExtString = A string formatted in the ISO Extended format for dates
3247 and times.
3248
3249 Throws:
3250 $(REF DateTimeException,std,datetime,date) if the given string is
3251 not in the ISO Extended format or if the resulting $(LREF DateTime)
3252 would not be valid.
3253 +/
3254 static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
3255 if (isSomeString!(S))
3256 {
3257 import std.algorithm.searching : countUntil;
3258 import std.exception : enforce;
3259 import std.format : format;
3260 import std.string : strip;
3261 import std.utf : byCodeUnit;
3262
3263 auto str = strip(isoExtString);
3264
3265 enforce!DateTimeException(str.length >= 15,
3266 format("Invalid format for DateTime.fromISOExtString: %s", isoExtString));
3267 auto t = str.byCodeUnit.countUntil('T');
3268
3269 enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromISOExtString: %s", isoExtString));
3270
3271 immutable date = Date.fromISOExtString(str[0 .. t]);
3272 immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3273
3274 return DateTime(date, tod);
3275 }
3276
3277 ///
3278 @safe unittest
3279 {
3280 assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
3281 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3282
3283 assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
3284 DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3285
3286 assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
3287 DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3288
3289 assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
3290 DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3291
3292 assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
3293 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3294 }
3295
3296 @safe unittest
3297 {
3298 assertThrown!DateTimeException(DateTime.fromISOExtString(""));
3299 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
3300 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
3301 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
3302 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
3303 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
3304
3305 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
3306 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3307 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3308 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
3309 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
3310 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
3311
3312 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
3313 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
3314 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
3315 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
3316
3317 assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
3318 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
3319
3320 assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3321 assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3322 assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3323 assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3324 assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3325 assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3326 assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3327 }
3328
3329 // https://issues.dlang.org/show_bug.cgi?id=17801
3330 @safe unittest
3331 {
3332 import std.conv : to;
3333 import std.meta : AliasSeq;
3334 static foreach (C; AliasSeq!(char, wchar, dchar))
3335 {
3336 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3337 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3338 }
3339 }
3340
3341
3342 /++
3343 Creates a $(LREF DateTime) from a string with the format
3344 YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
3345
3346 Params:
3347 simpleString = A string formatted in the way that toSimpleString
3348 formats dates and times.
3349
3350 Throws:
3351 $(REF DateTimeException,std,datetime,date) if the given string is
3352 not in the correct format or if the resulting $(LREF DateTime)
3353 would not be valid.
3354 +/
3355 static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
3356 if (isSomeString!(S))
3357 {
3358 import std.algorithm.searching : countUntil;
3359 import std.exception : enforce;
3360 import std.format : format;
3361 import std.string : strip;
3362 import std.utf : byCodeUnit;
3363
3364 auto str = strip(simpleString);
3365
3366 enforce!DateTimeException(str.length >= 15,
3367 format("Invalid format for DateTime.fromSimpleString: %s", simpleString));
3368 auto t = str.byCodeUnit.countUntil(' ');
3369
3370 enforce!DateTimeException(t != -1, format("Invalid format for DateTime.fromSimpleString: %s", simpleString));
3371
3372 immutable date = Date.fromSimpleString(str[0 .. t]);
3373 immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3374
3375 return DateTime(date, tod);
3376 }
3377
3378 ///
3379 @safe unittest
3380 {
3381 assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
3382 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3383 assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
3384 DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3385 assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
3386 DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3387 assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
3388 DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3389 assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
3390 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3391 }
3392
3393 @safe unittest
3394 {
3395 assertThrown!DateTimeException(DateTime.fromISOString(""));
3396 assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3397 assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3398 assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3399 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3400 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3401
3402 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3403 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3404 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3405 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3406 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3407
3408 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3409 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3410 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3411 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3412 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3413 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3414
3415 assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
3416 assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
3417
3418 assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
3419 DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
3420 assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
3421 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3422 assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
3423 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3424 assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
3425 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3426 assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
3427 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3428 assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
3429 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3430 assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
3431 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3432 }
3433
3434 // https://issues.dlang.org/show_bug.cgi?id=17801
3435 @safe unittest
3436 {
3437 import std.conv : to;
3438 import std.meta : AliasSeq;
3439 static foreach (C; AliasSeq!(char, wchar, dchar))
3440 {
3441 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
3442 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3443 }
3444 }
3445
3446
3447 /++
3448 Returns the $(LREF DateTime) farthest in the past which is representable
3449 by $(LREF DateTime).
3450 +/
3451 @property static DateTime min() @safe pure nothrow @nogc
3452 out(result)
3453 {
3454 assert(result._date == Date.min);
3455 assert(result._tod == TimeOfDay.min);
3456 }
3457 do
3458 {
3459 auto dt = DateTime.init;
3460 dt._date._year = short.min;
3461 dt._date._month = Month.jan;
3462 dt._date._day = 1;
3463
3464 return dt;
3465 }
3466
3467 @safe unittest
3468 {
3469 assert(DateTime.min.year < 0);
3470 assert(DateTime.min < DateTime.max);
3471 }
3472
3473
3474 /++
3475 Returns the $(LREF DateTime) farthest in the future which is
3476 representable by $(LREF DateTime).
3477 +/
3478 @property static DateTime max() @safe pure nothrow @nogc
3479 out(result)
3480 {
3481 assert(result._date == Date.max);
3482 assert(result._tod == TimeOfDay.max);
3483 }
3484 do
3485 {
3486 auto dt = DateTime.init;
3487 dt._date._year = short.max;
3488 dt._date._month = Month.dec;
3489 dt._date._day = 31;
3490 dt._tod._hour = TimeOfDay.maxHour;
3491 dt._tod._minute = TimeOfDay.maxMinute;
3492 dt._tod._second = TimeOfDay.maxSecond;
3493
3494 return dt;
3495 }
3496
3497 @safe unittest
3498 {
3499 assert(DateTime.max.year > 0);
3500 assert(DateTime.max > DateTime.min);
3501 }
3502
3503
3504 private:
3505
3506 /+
3507 Add seconds to the time of day. Negative values will subtract. If the
3508 number of seconds overflows (or underflows), then the seconds will wrap,
3509 increasing (or decreasing) the number of minutes accordingly. The
3510 same goes for any larger units.
3511
3512 Params:
3513 seconds = The number of seconds to add to this $(LREF DateTime).
3514 +/
3515 ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
3516 {
3517 import core.time : convert;
3518 long hnsecs = convert!("seconds", "hnsecs")(seconds);
3519 hnsecs += convert!("hours", "hnsecs")(_tod._hour);
3520 hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
3521 hnsecs += convert!("seconds", "hnsecs")(_tod._second);
3522
3523 auto days = splitUnitsFromHNSecs!"days"(hnsecs);
3524
3525 if (hnsecs < 0)
3526 {
3527 hnsecs += convert!("days", "hnsecs")(1);
3528 --days;
3529 }
3530
3531 _date._addDays(days);
3532
3533 immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
3534 immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
3535 immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
3536
3537 _tod._hour = cast(ubyte) newHours;
3538 _tod._minute = cast(ubyte) newMinutes;
3539 _tod._second = cast(ubyte) newSeconds;
3540
3541 return this;
3542 }
3543
3544 @safe unittest
3545 {
3546 static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
3547 {
3548 orig._addSeconds(seconds);
3549 assert(orig == expected);
3550 }
3551
3552 // Test A.D.
3553 testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
3554 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
3555 testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
3556 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
3557 testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
3558 testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
3559 testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
3560 testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
3561 testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
3562 testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
3563 testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
3564 testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
3565 testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
3566 testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
3567
3568 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
3569 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
3570 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
3571 testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
3572 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
3573 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
3574 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
3575 testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
3576 testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
3577
3578 testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
3579 testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
3580 testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
3581 testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
3582 testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
3583 testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
3584 testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
3585 testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
3586 testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
3587 testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
3588 testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
3589 testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
3590 testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
3591
3592 testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
3593 testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
3594 testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
3595 testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
3596 testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
3597 testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
3598
3599 testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
3600 testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
3601 testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
3602
3603 testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
3604 testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
3605 testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
3606
3607 testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
3608 testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
3609 testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
3610
3611 testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
3612 testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
3613 testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
3614
3615 testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
3616 testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
3617 testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
3618
3619 testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
3620 testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
3621 testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
3622
3623 // Test B.C.
3624 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
3625 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
3626 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
3627 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
3628 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
3629 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
3630 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
3631 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
3632 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
3633 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
3634 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
3635 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
3636 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
3637 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
3638
3639 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
3640 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
3641 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
3642 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
3643 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
3644 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
3645 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
3646 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
3647 testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
3648
3649 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
3650 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
3651 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
3652 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
3653 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
3654 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
3655 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
3656 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
3657 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
3658 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
3659 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
3660 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
3661 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
3662
3663 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
3664 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
3665 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
3666 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
3667 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
3668 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
3669 testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
3670
3671 testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
3672 testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
3673 testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
3674
3675 testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
3676 testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
3677 testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
3678
3679 testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
3680 testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
3681 testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
3682
3683 testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
3684 testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
3685 testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
3686
3687 testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
3688 testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
3689 testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
3690
3691 testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
3692 testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
3693 testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
3694
3695 // Test Both
3696 testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
3697 testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
3698
3699 testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
3700 testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
3701
3702 testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
3703 testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
3704
3705 testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
3706 testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
3707
3708 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3709 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3710 static assert(!__traits(compiles, cdt._addSeconds(4)));
3711 static assert(!__traits(compiles, idt._addSeconds(4)));
3712 }
3713
3714
3715 Date _date;
3716 TimeOfDay _tod;
3717 }
3718
3719 ///
3720 @safe pure unittest
3721 {
3722 import core.time : days, seconds;
3723
3724 auto dt = DateTime(2000, 6, 1, 10, 30, 0);
3725
3726 assert(dt.date == Date(2000, 6, 1));
3727 assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
3728 assert(dt.dayOfYear == 153);
3729 assert(dt.dayOfWeek == DayOfWeek.thu);
3730
3731 dt += 10.days + 100.seconds;
3732 assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
3733
3734 assert(dt.toISOExtString() == "2000-06-11T10:31:40");
3735 assert(dt.toISOString() == "20000611T103140");
3736 assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
3737
3738 assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3739 assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
3740 assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3741 }
3742
3743 /++
3744 Represents a date in the
3745 $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
3746 Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
3747 are A.D. Non-positive years are B.C.
3748
3749 Year, month, and day are kept separately internally so that `Date` is
3750 optimized for calendar-based operations.
3751
3752 `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
3753 leap year calculations for its entire length. As per
3754 $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
3755 year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
3756 as a positive integer with 1 B.C. being the year prior to 1 A.D.
3757
3758 Year 0 is a leap year.
3759 +/
3760 struct Date
3761 {
3762 public:
3763
3764 /++
3765 Throws:
3766 $(REF DateTimeException,std,datetime,date) if the resulting
3767 $(LREF Date) would not be valid.
3768
3769 Params:
3770 year = Year of the Gregorian Calendar. Positive values are A.D.
3771 Non-positive values are B.C. with year 0 being the year
3772 prior to 1 A.D.
3773 month = Month of the year (January is 1).
3774 day = Day of the month.
3775 +/
3776 this(int year, int month, int day) @safe pure
3777 {
3778 enforceValid!"months"(cast(Month) month);
3779 enforceValid!"days"(year, cast(Month) month, day);
3780
3781 _year = year.castToYear;
3782 _month = cast(Month) month;
3783 _day = cast(ubyte) day;
3784 }
3785
3786 @safe unittest
3787 {
3788 import std.exception : assertNotThrown;
3789 assert(Date(1, 1, 1) == Date.init);
3790
3791 static void testDate(Date date, int year, int month, int day)
3792 {
3793 assert(date._year == year);
3794 assert(date._month == month);
3795 assert(date._day == day);
3796 }
3797
3798 testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
3799 testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
3800 testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
3801
3802 // Test A.D.
3803 assertThrown!DateTimeException(Date(1, 0, 1));
3804 assertThrown!DateTimeException(Date(1, 1, 0));
3805 assertThrown!DateTimeException(Date(1999, 13, 1));
3806 assertThrown!DateTimeException(Date(1999, 1, 32));
3807 assertThrown!DateTimeException(Date(1999, 2, 29));
3808 assertThrown!DateTimeException(Date(2000, 2, 30));
3809 assertThrown!DateTimeException(Date(1999, 3, 32));
3810 assertThrown!DateTimeException(Date(1999, 4, 31));
3811 assertThrown!DateTimeException(Date(1999, 5, 32));
3812 assertThrown!DateTimeException(Date(1999, 6, 31));
3813 assertThrown!DateTimeException(Date(1999, 7, 32));
3814 assertThrown!DateTimeException(Date(1999, 8, 32));
3815 assertThrown!DateTimeException(Date(1999, 9, 31));
3816 assertThrown!DateTimeException(Date(1999, 10, 32));
3817 assertThrown!DateTimeException(Date(1999, 11, 31));
3818 assertThrown!DateTimeException(Date(1999, 12, 32));
3819 assertThrown!DateTimeException(Date(short.max+1, 1, 1));
3820
3821 assertNotThrown!DateTimeException(Date(1999, 1, 31));
3822 assertNotThrown!DateTimeException(Date(1999, 2, 28));
3823 assertNotThrown!DateTimeException(Date(2000, 2, 29));
3824 assertNotThrown!DateTimeException(Date(1999, 3, 31));
3825 assertNotThrown!DateTimeException(Date(1999, 4, 30));
3826 assertNotThrown!DateTimeException(Date(1999, 5, 31));
3827 assertNotThrown!DateTimeException(Date(1999, 6, 30));
3828 assertNotThrown!DateTimeException(Date(1999, 7, 31));
3829 assertNotThrown!DateTimeException(Date(1999, 8, 31));
3830 assertNotThrown!DateTimeException(Date(1999, 9, 30));
3831 assertNotThrown!DateTimeException(Date(1999, 10, 31));
3832 assertNotThrown!DateTimeException(Date(1999, 11, 30));
3833 assertNotThrown!DateTimeException(Date(1999, 12, 31));
3834
3835 // Test B.C.
3836 assertNotThrown!DateTimeException(Date(0, 1, 1));
3837 assertNotThrown!DateTimeException(Date(-1, 1, 1));
3838 assertNotThrown!DateTimeException(Date(-1, 12, 31));
3839 assertNotThrown!DateTimeException(Date(-1, 2, 28));
3840 assertNotThrown!DateTimeException(Date(-4, 2, 29));
3841
3842 assertThrown!DateTimeException(Date(-1, 2, 29));
3843 assertThrown!DateTimeException(Date(-2, 2, 29));
3844 assertThrown!DateTimeException(Date(-3, 2, 29));
3845 assertThrown!DateTimeException(Date(short.min-1, 1, 1));
3846 }
3847
3848
3849 /++
3850 Params:
3851 day = The Xth day of the Gregorian Calendar that the constructed
3852 $(LREF Date) will be for.
3853 +/
3854 this(int day) @safe pure nothrow @nogc
3855 {
3856 if (day > 0)
3857 {
3858 int years = (day / daysIn400Years) * 400 + 1;
3859 day %= daysIn400Years;
3860
3861 {
3862 immutable tempYears = day / daysIn100Years;
3863
3864 if (tempYears == 4)
3865 {
3866 years += 300;
3867 day -= daysIn100Years * 3;
3868 }
3869 else
3870 {
3871 years += tempYears * 100;
3872 day %= daysIn100Years;
3873 }
3874 }
3875
3876 years += (day / daysIn4Years) * 4;
3877 day %= daysIn4Years;
3878
3879 {
3880 immutable tempYears = day / daysInYear;
3881
3882 if (tempYears == 4)
3883 {
3884 years += 3;
3885 day -= daysInYear * 3;
3886 }
3887 else
3888 {
3889 years += tempYears;
3890 day %= daysInYear;
3891 }
3892 }
3893
3894 if (day == 0)
3895 {
3896 _year = cast(short)(years - 1);
3897 _month = Month.dec;
3898 _day = 31;
3899 }
3900 else
3901 {
3902 _year = cast(short) years;
3903
3904 setDayOfYear(day);
3905 }
3906 }
3907 else if (day <= 0 && -day < daysInLeapYear)
3908 {
3909 _year = 0;
3910
3911 setDayOfYear(daysInLeapYear + day);
3912 }
3913 else
3914 {
3915 day += daysInLeapYear - 1;
3916 int years = (day / daysIn400Years) * 400 - 1;
3917 day %= daysIn400Years;
3918
3919 {
3920 immutable tempYears = day / daysIn100Years;
3921
3922 if (tempYears == -4)
3923 {
3924 years -= 300;
3925 day += daysIn100Years * 3;
3926 }
3927 else
3928 {
3929 years += tempYears * 100;
3930 day %= daysIn100Years;
3931 }
3932 }
3933
3934 years += (day / daysIn4Years) * 4;
3935 day %= daysIn4Years;
3936
3937 {
3938 immutable tempYears = day / daysInYear;
3939
3940 if (tempYears == -4)
3941 {
3942 years -= 3;
3943 day += daysInYear * 3;
3944 }
3945 else
3946 {
3947 years += tempYears;
3948 day %= daysInYear;
3949 }
3950 }
3951
3952 if (day == 0)
3953 {
3954 _year = cast(short)(years + 1);
3955 _month = Month.jan;
3956 _day = 1;
3957 }
3958 else
3959 {
3960 _year = cast(short) years;
3961 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
3962
3963 setDayOfYear(newDoY);
3964 }
3965 }
3966 }
3967
3968 @safe unittest
3969 {
3970 import std.range : chain;
3971
3972 // Test A.D.
3973 foreach (gd; chain(testGregDaysBC, testGregDaysAD))
3974 assert(Date(gd.day) == gd.date);
3975 }
3976
3977
3978 /++
3979 Compares this $(LREF Date) with the given $(LREF Date).
3980
3981 Returns:
3982 $(BOOKTABLE,
3983 $(TR $(TD this < rhs) $(TD < 0))
3984 $(TR $(TD this == rhs) $(TD 0))
3985 $(TR $(TD this > rhs) $(TD > 0))
3986 )
3987 +/
3988 int opCmp(Date rhs) const @safe pure nothrow @nogc
3989 {
3990 if (_year < rhs._year)
3991 return -1;
3992 if (_year > rhs._year)
3993 return 1;
3994
3995 if (_month < rhs._month)
3996 return -1;
3997 if (_month > rhs._month)
3998 return 1;
3999
4000 if (_day < rhs._day)
4001 return -1;
4002 if (_day > rhs._day)
4003 return 1;
4004
4005 return 0;
4006 }
4007
4008 @safe unittest
4009 {
4010 // Test A.D.
4011 assert(Date(1, 1, 1).opCmp(Date.init) == 0);
4012
4013 assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
4014 assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
4015 assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
4016
4017 assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
4018 assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
4019
4020 assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
4021
4022 assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
4023 assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
4024 assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
4025 assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
4026 assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
4027 assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
4028
4029 assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
4030 assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4031 assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
4032 assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
4033 assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
4034 assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4035
4036 // Test B.C.
4037 assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
4038 assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
4039 assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
4040 assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
4041
4042 assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
4043 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
4044
4045 assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
4046
4047 assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
4048 assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
4049 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
4050 assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
4051 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4052 assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
4053
4054 assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
4055 assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
4056 assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4057 assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
4058 assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
4059 assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
4060
4061 // Test Both
4062 assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
4063 assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
4064
4065 assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
4066 assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
4067
4068 assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
4069 assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
4070
4071 assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
4072 assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
4073
4074 assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
4075 assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
4076
4077 auto date = Date(1999, 7, 6);
4078 const cdate = Date(1999, 7, 6);
4079 immutable idate = Date(1999, 7, 6);
4080 assert(date.opCmp(date) == 0);
4081 assert(date.opCmp(cdate) == 0);
4082 assert(date.opCmp(idate) == 0);
4083 assert(cdate.opCmp(date) == 0);
4084 assert(cdate.opCmp(cdate) == 0);
4085 assert(cdate.opCmp(idate) == 0);
4086 assert(idate.opCmp(date) == 0);
4087 assert(idate.opCmp(cdate) == 0);
4088 assert(idate.opCmp(idate) == 0);
4089 }
4090
4091
4092 /++
4093 Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4094 are B.C.
4095 +/
4096 @property short year() const @safe pure nothrow @nogc
4097 {
4098 return _year;
4099 }
4100
4101 ///
4102 @safe unittest
4103 {
4104 assert(Date(1999, 7, 6).year == 1999);
4105 assert(Date(2010, 10, 4).year == 2010);
4106 assert(Date(-7, 4, 5).year == -7);
4107 }
4108
4109 @safe unittest
4110 {
4111 assert(Date.init.year == 1);
4112 assert(Date(1999, 7, 6).year == 1999);
4113 assert(Date(-1999, 7, 6).year == -1999);
4114
4115 const cdate = Date(1999, 7, 6);
4116 immutable idate = Date(1999, 7, 6);
4117 assert(cdate.year == 1999);
4118 assert(idate.year == 1999);
4119 }
4120
4121 /++
4122 Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4123 are B.C.
4124
4125 Params:
4126 year = The year to set this Date's year to.
4127
4128 Throws:
4129 $(REF DateTimeException,std,datetime,date) if the new year is not
4130 a leap year and the resulting date would be on February 29th.
4131 +/
4132 @property void year(int year) @safe pure
4133 {
4134 enforceValid!"days"(year, _month, _day);
4135 _year = year.castToYear;
4136 }
4137
4138 ///
4139 @safe unittest
4140 {
4141 assert(Date(1999, 7, 6).year == 1999);
4142 assert(Date(2010, 10, 4).year == 2010);
4143 assert(Date(-7, 4, 5).year == -7);
4144 }
4145
4146 @safe unittest
4147 {
4148 static void testDateInvalid(Date date, int year)
4149 {
4150 date.year = year;
4151 }
4152
4153 static void testDate(Date date, int year, Date expected)
4154 {
4155 date.year = year;
4156 assert(date == expected);
4157 }
4158
4159 assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
4160
4161 testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
4162 testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
4163 testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
4164
4165 const cdate = Date(1999, 7, 6);
4166 immutable idate = Date(1999, 7, 6);
4167 static assert(!__traits(compiles, cdate.year = 1999));
4168 static assert(!__traits(compiles, idate.year = 1999));
4169 }
4170
4171
4172 /++
4173 Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4174
4175 Throws:
4176 $(REF DateTimeException,std,datetime,date) if `isAD` is true.
4177 +/
4178 @property ushort yearBC() const @safe pure
4179 {
4180 import std.format : format;
4181
4182 if (isAD)
4183 throw new DateTimeException(format("Year %s is A.D.", _year));
4184 return cast(ushort)((_year * -1) + 1);
4185 }
4186
4187 ///
4188 @safe unittest
4189 {
4190 assert(Date(0, 1, 1).yearBC == 1);
4191 assert(Date(-1, 1, 1).yearBC == 2);
4192 assert(Date(-100, 1, 1).yearBC == 101);
4193 }
4194
4195 @safe unittest
4196 {
4197 assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
4198
4199 auto date = Date(0, 7, 6);
4200 const cdate = Date(0, 7, 6);
4201 immutable idate = Date(0, 7, 6);
4202 assert(date.yearBC == 1);
4203 assert(cdate.yearBC == 1);
4204 assert(idate.yearBC == 1);
4205 }
4206
4207
4208 /++
4209 Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4210
4211 Params:
4212 year = The year B.C. to set this $(LREF Date)'s year to.
4213
4214 Throws:
4215 $(REF DateTimeException,std,datetime,date) if a non-positive value
4216 is given.
4217 +/
4218 @property void yearBC(int year) @safe pure
4219 {
4220 if (year <= 0)
4221 throw new DateTimeException("The given year is not a year B.C.");
4222 _year = castToYear((year - 1) * -1);
4223 }
4224
4225 ///
4226 @safe unittest
4227 {
4228 auto date = Date(2010, 1, 1);
4229 date.yearBC = 1;
4230 assert(date == Date(0, 1, 1));
4231
4232 date.yearBC = 10;
4233 assert(date == Date(-9, 1, 1));
4234 }
4235
4236 @safe unittest
4237 {
4238 assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
4239
4240 auto date = Date(0, 7, 6);
4241 const cdate = Date(0, 7, 6);
4242 immutable idate = Date(0, 7, 6);
4243 date.yearBC = 7;
4244 assert(date.yearBC == 7);
4245 static assert(!__traits(compiles, cdate.yearBC = 7));
4246 static assert(!__traits(compiles, idate.yearBC = 7));
4247 }
4248
4249
4250 /++
4251 Month of a Gregorian Year.
4252 +/
4253 @property Month month() const @safe pure nothrow @nogc
4254 {
4255 return _month;
4256 }
4257
4258 ///
4259 @safe unittest
4260 {
4261 assert(Date(1999, 7, 6).month == 7);
4262 assert(Date(2010, 10, 4).month == 10);
4263 assert(Date(-7, 4, 5).month == 4);
4264 }
4265
4266 @safe unittest
4267 {
4268 assert(Date.init.month == 1);
4269 assert(Date(1999, 7, 6).month == 7);
4270 assert(Date(-1999, 7, 6).month == 7);
4271
4272 const cdate = Date(1999, 7, 6);
4273 immutable idate = Date(1999, 7, 6);
4274 assert(cdate.month == 7);
4275 assert(idate.month == 7);
4276 }
4277
4278 /++
4279 Month of a Gregorian Year.
4280
4281 Params:
4282 month = The month to set this $(LREF Date)'s month to.
4283
4284 Throws:
4285 $(REF DateTimeException,std,datetime,date) if the given month is
4286 not a valid month or if the current day would not be valid in the
4287 given month.
4288 +/
4289 @property void month(Month month) @safe pure
4290 {
4291 enforceValid!"months"(month);
4292 enforceValid!"days"(_year, month, _day);
4293 _month = cast(Month) month;
4294 }
4295
4296 @safe unittest
4297 {
4298 static void testDate(Date date, Month month, Date expected = Date.init)
4299 {
4300 date.month = month;
4301 assert(expected != Date.init);
4302 assert(date == expected);
4303 }
4304
4305 assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
4306 assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
4307 assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
4308 assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
4309
4310 testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
4311 testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
4312
4313 const cdate = Date(1999, 7, 6);
4314 immutable idate = Date(1999, 7, 6);
4315 static assert(!__traits(compiles, cdate.month = 7));
4316 static assert(!__traits(compiles, idate.month = 7));
4317 }
4318
4319
4320 /++
4321 Day of a Gregorian Month.
4322 +/
4323 @property ubyte day() const @safe pure nothrow @nogc
4324 {
4325 return _day;
4326 }
4327
4328 ///
4329 @safe unittest
4330 {
4331 assert(Date(1999, 7, 6).day == 6);
4332 assert(Date(2010, 10, 4).day == 4);
4333 assert(Date(-7, 4, 5).day == 5);
4334 }
4335
4336 @safe unittest
4337 {
4338 import std.format : format;
4339 import std.range : chain;
4340
4341 static void test(Date date, int expected)
4342 {
4343 assert(date.day == expected, format("Value given: %s", date));
4344 }
4345
4346 foreach (year; chain(testYearsBC, testYearsAD))
4347 {
4348 foreach (md; testMonthDays)
4349 test(Date(year, md.month, md.day), md.day);
4350 }
4351
4352 const cdate = Date(1999, 7, 6);
4353 immutable idate = Date(1999, 7, 6);
4354 assert(cdate.day == 6);
4355 assert(idate.day == 6);
4356 }
4357
4358 /++
4359 Day of a Gregorian Month.
4360
4361 Params:
4362 day = The day of the month to set this $(LREF Date)'s day to.
4363
4364 Throws:
4365 $(REF DateTimeException,std,datetime,date) if the given day is not
4366 a valid day of the current month.
4367 +/
4368 @property void day(int day) @safe pure
4369 {
4370 enforceValid!"days"(_year, _month, day);
4371 _day = cast(ubyte) day;
4372 }
4373
4374 @safe unittest
4375 {
4376 import std.exception : assertNotThrown;
4377
4378 static void testDate(Date date, int day)
4379 {
4380 date.day = day;
4381 }
4382
4383 // Test A.D.
4384 assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
4385 assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
4386 assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
4387 assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
4388 assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
4389 assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
4390 assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
4391 assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
4392 assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
4393 assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
4394 assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
4395 assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
4396 assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
4397 assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
4398
4399 assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
4400 assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
4401 assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
4402 assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
4403 assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
4404 assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
4405 assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
4406 assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
4407 assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
4408 assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
4409 assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
4410 assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
4411 assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
4412
4413 {
4414 auto date = Date(1, 1, 1);
4415 date.day = 6;
4416 assert(date == Date(1, 1, 6));
4417 }
4418
4419 // Test B.C.
4420 assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
4421 assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
4422 assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
4423 assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
4424 assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
4425 assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
4426 assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
4427 assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
4428 assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
4429 assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
4430 assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
4431 assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
4432 assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
4433 assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
4434
4435 assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
4436 assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
4437 assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
4438 assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
4439 assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
4440 assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
4441 assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
4442 assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
4443 assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
4444 assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
4445 assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
4446 assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
4447 assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
4448
4449 {
4450 auto date = Date(-1, 1, 1);
4451 date.day = 6;
4452 assert(date == Date(-1, 1, 6));
4453 }
4454
4455 const cdate = Date(1999, 7, 6);
4456 immutable idate = Date(1999, 7, 6);
4457 static assert(!__traits(compiles, cdate.day = 6));
4458 static assert(!__traits(compiles, idate.day = 6));
4459 }
4460
4461
4462 /++
4463 Adds the given number of years or months to this $(LREF Date), mutating
4464 it. A negative number will subtract.
4465
4466 Note that if day overflow is allowed, and the date with the adjusted
4467 year/month overflows the number of days in the new month, then the month
4468 will be incremented by one, and the day set to the number of days
4469 overflowed. (e.g. if the day were 31 and the new month were June, then
4470 the month would be incremented to July, and the new day would be 1). If
4471 day overflow is not allowed, then the day will be set to the last valid
4472 day in the month (e.g. June 31st would become June 30th).
4473
4474 Params:
4475 units = The type of units to add ("years" or "months").
4476 value = The number of months or years to add to this
4477 $(LREF Date).
4478 allowOverflow = Whether the day should be allowed to overflow,
4479 causing the month to increment.
4480
4481 Returns:
4482 A reference to the `Date` (`this`).
4483 +/
4484 @safe pure nothrow @nogc
4485 ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4486 if (units == "years")
4487 {
4488 _year += value;
4489
4490 if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
4491 {
4492 if (allowOverflow == AllowDayOverflow.yes)
4493 {
4494 _month = Month.mar;
4495 _day = 1;
4496 }
4497 else
4498 _day = 28;
4499 }
4500
4501 return this;
4502 }
4503
4504 ///
4505 @safe unittest
4506 {
4507 auto d1 = Date(2010, 1, 1);
4508 d1.add!"months"(11);
4509 assert(d1 == Date(2010, 12, 1));
4510
4511 auto d2 = Date(2010, 1, 1);
4512 d2.add!"months"(-11);
4513 assert(d2 == Date(2009, 2, 1));
4514
4515 auto d3 = Date(2000, 2, 29);
4516 d3.add!"years"(1);
4517 assert(d3 == Date(2001, 3, 1));
4518
4519 auto d4 = Date(2000, 2, 29);
4520 d4.add!"years"(1, AllowDayOverflow.no);
4521 assert(d4 == Date(2001, 2, 28));
4522 }
4523
4524 // Test add!"years"() with AllowDayOverflow.yes
4525 @safe unittest
4526 {
4527 // Test A.D.
4528 {
4529 auto date = Date(1999, 7, 6);
4530 date.add!"years"(7);
4531 assert(date == Date(2006, 7, 6));
4532 date.add!"years"(-9);
4533 assert(date == Date(1997, 7, 6));
4534 }
4535
4536 {
4537 auto date = Date(1999, 2, 28);
4538 date.add!"years"(1);
4539 assert(date == Date(2000, 2, 28));
4540 }
4541
4542 {
4543 auto date = Date(2000, 2, 29);
4544 date.add!"years"(-1);
4545 assert(date == Date(1999, 3, 1));
4546 }
4547
4548 // Test B.C.
4549 {
4550 auto date = Date(-1999, 7, 6);
4551 date.add!"years"(-7);
4552 assert(date == Date(-2006, 7, 6));
4553 date.add!"years"(9);
4554 assert(date == Date(-1997, 7, 6));
4555 }
4556
4557 {
4558 auto date = Date(-1999, 2, 28);
4559 date.add!"years"(-1);
4560 assert(date == Date(-2000, 2, 28));
4561 }
4562
4563 {
4564 auto date = Date(-2000, 2, 29);
4565 date.add!"years"(1);
4566 assert(date == Date(-1999, 3, 1));
4567 }
4568
4569 // Test Both
4570 {
4571 auto date = Date(4, 7, 6);
4572 date.add!"years"(-5);
4573 assert(date == Date(-1, 7, 6));
4574 date.add!"years"(5);
4575 assert(date == Date(4, 7, 6));
4576 }
4577
4578 {
4579 auto date = Date(-4, 7, 6);
4580 date.add!"years"(5);
4581 assert(date == Date(1, 7, 6));
4582 date.add!"years"(-5);
4583 assert(date == Date(-4, 7, 6));
4584 }
4585
4586 {
4587 auto date = Date(4, 7, 6);
4588 date.add!"years"(-8);
4589 assert(date == Date(-4, 7, 6));
4590 date.add!"years"(8);
4591 assert(date == Date(4, 7, 6));
4592 }
4593
4594 {
4595 auto date = Date(-4, 7, 6);
4596 date.add!"years"(8);
4597 assert(date == Date(4, 7, 6));
4598 date.add!"years"(-8);
4599 assert(date == Date(-4, 7, 6));
4600 }
4601
4602 {
4603 auto date = Date(-4, 2, 29);
4604 date.add!"years"(5);
4605 assert(date == Date(1, 3, 1));
4606 }
4607
4608 {
4609 auto date = Date(4, 2, 29);
4610 date.add!"years"(-5);
4611 assert(date == Date(-1, 3, 1));
4612 }
4613
4614 {
4615 auto date = Date(4, 2, 29);
4616 date.add!"years"(-5).add!"years"(7);
4617 assert(date == Date(6, 3, 1));
4618 }
4619
4620 const cdate = Date(1999, 7, 6);
4621 immutable idate = Date(1999, 7, 6);
4622 static assert(!__traits(compiles, cdate.add!"years"(7)));
4623 static assert(!__traits(compiles, idate.add!"years"(7)));
4624 }
4625
4626 // Test add!"years"() with AllowDayOverflow.no
4627 @safe unittest
4628 {
4629 // Test A.D.
4630 {
4631 auto date = Date(1999, 7, 6);
4632 date.add!"years"(7, AllowDayOverflow.no);
4633 assert(date == Date(2006, 7, 6));
4634 date.add!"years"(-9, AllowDayOverflow.no);
4635 assert(date == Date(1997, 7, 6));
4636 }
4637
4638 {
4639 auto date = Date(1999, 2, 28);
4640 date.add!"years"(1, AllowDayOverflow.no);
4641 assert(date == Date(2000, 2, 28));
4642 }
4643
4644 {
4645 auto date = Date(2000, 2, 29);
4646 date.add!"years"(-1, AllowDayOverflow.no);
4647 assert(date == Date(1999, 2, 28));
4648 }
4649
4650 // Test B.C.
4651 {
4652 auto date = Date(-1999, 7, 6);
4653 date.add!"years"(-7, AllowDayOverflow.no);
4654 assert(date == Date(-2006, 7, 6));
4655 date.add!"years"(9, AllowDayOverflow.no);
4656 assert(date == Date(-1997, 7, 6));
4657 }
4658
4659 {
4660 auto date = Date(-1999, 2, 28);
4661 date.add!"years"(-1, AllowDayOverflow.no);
4662 assert(date == Date(-2000, 2, 28));
4663 }
4664
4665 {
4666 auto date = Date(-2000, 2, 29);
4667 date.add!"years"(1, AllowDayOverflow.no);
4668 assert(date == Date(-1999, 2, 28));
4669 }
4670
4671 // Test Both
4672 {
4673 auto date = Date(4, 7, 6);
4674 date.add!"years"(-5, AllowDayOverflow.no);
4675 assert(date == Date(-1, 7, 6));
4676 date.add!"years"(5, AllowDayOverflow.no);
4677 assert(date == Date(4, 7, 6));
4678 }
4679
4680 {
4681 auto date = Date(-4, 7, 6);
4682 date.add!"years"(5, AllowDayOverflow.no);
4683 assert(date == Date(1, 7, 6));
4684 date.add!"years"(-5, AllowDayOverflow.no);
4685 assert(date == Date(-4, 7, 6));
4686 }
4687
4688 {
4689 auto date = Date(4, 7, 6);
4690 date.add!"years"(-8, AllowDayOverflow.no);
4691 assert(date == Date(-4, 7, 6));
4692 date.add!"years"(8, AllowDayOverflow.no);
4693 assert(date == Date(4, 7, 6));
4694 }
4695
4696 {
4697 auto date = Date(-4, 7, 6);
4698 date.add!"years"(8, AllowDayOverflow.no);
4699 assert(date == Date(4, 7, 6));
4700 date.add!"years"(-8, AllowDayOverflow.no);
4701 assert(date == Date(-4, 7, 6));
4702 }
4703
4704 {
4705 auto date = Date(-4, 2, 29);
4706 date.add!"years"(5, AllowDayOverflow.no);
4707 assert(date == Date(1, 2, 28));
4708 }
4709
4710 {
4711 auto date = Date(4, 2, 29);
4712 date.add!"years"(-5, AllowDayOverflow.no);
4713 assert(date == Date(-1, 2, 28));
4714 }
4715
4716 {
4717 auto date = Date(4, 2, 29);
4718 date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
4719 assert(date == Date(6, 2, 28));
4720 }
4721 }
4722
4723
4724 // Shares documentation with "years" version.
4725 @safe pure nothrow @nogc
4726 ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4727 if (units == "months")
4728 {
4729 auto years = months / 12;
4730 months %= 12;
4731 auto newMonth = _month + months;
4732
4733 if (months < 0)
4734 {
4735 if (newMonth < 1)
4736 {
4737 newMonth += 12;
4738 --years;
4739 }
4740 }
4741 else if (newMonth > 12)
4742 {
4743 newMonth -= 12;
4744 ++years;
4745 }
4746
4747 _year += years;
4748 _month = cast(Month) newMonth;
4749
4750 immutable currMaxDay = maxDay(_year, _month);
4751 immutable overflow = _day - currMaxDay;
4752
4753 if (overflow > 0)
4754 {
4755 if (allowOverflow == AllowDayOverflow.yes)
4756 {
4757 ++_month;
4758 _day = cast(ubyte) overflow;
4759 }
4760 else
4761 _day = cast(ubyte) currMaxDay;
4762 }
4763
4764 return this;
4765 }
4766
4767 // Test add!"months"() with AllowDayOverflow.yes
4768 @safe unittest
4769 {
4770 // Test A.D.
4771 {
4772 auto date = Date(1999, 7, 6);
4773 date.add!"months"(3);
4774 assert(date == Date(1999, 10, 6));
4775 date.add!"months"(-4);
4776 assert(date == Date(1999, 6, 6));
4777 }
4778
4779 {
4780 auto date = Date(1999, 7, 6);
4781 date.add!"months"(6);
4782 assert(date == Date(2000, 1, 6));
4783 date.add!"months"(-6);
4784 assert(date == Date(1999, 7, 6));
4785 }
4786
4787 {
4788 auto date = Date(1999, 7, 6);
4789 date.add!"months"(27);
4790 assert(date == Date(2001, 10, 6));
4791 date.add!"months"(-28);
4792 assert(date == Date(1999, 6, 6));
4793 }
4794
4795 {
4796 auto date = Date(1999, 5, 31);
4797 date.add!"months"(1);
4798 assert(date == Date(1999, 7, 1));
4799 }
4800
4801 {
4802 auto date = Date(1999, 5, 31);
4803 date.add!"months"(-1);
4804 assert(date == Date(1999, 5, 1));
4805 }
4806
4807 {
4808 auto date = Date(1999, 2, 28);
4809 date.add!"months"(12);
4810 assert(date == Date(2000, 2, 28));
4811 }
4812
4813 {
4814 auto date = Date(2000, 2, 29);
4815 date.add!"months"(12);
4816 assert(date == Date(2001, 3, 1));
4817 }
4818
4819 {
4820 auto date = Date(1999, 7, 31);
4821 date.add!"months"(1);
4822 assert(date == Date(1999, 8, 31));
4823 date.add!"months"(1);
4824 assert(date == Date(1999, 10, 1));
4825 }
4826
4827 {
4828 auto date = Date(1998, 8, 31);
4829 date.add!"months"(13);
4830 assert(date == Date(1999, 10, 1));
4831 date.add!"months"(-13);
4832 assert(date == Date(1998, 9, 1));
4833 }
4834
4835 {
4836 auto date = Date(1997, 12, 31);
4837 date.add!"months"(13);
4838 assert(date == Date(1999, 1, 31));
4839 date.add!"months"(-13);
4840 assert(date == Date(1997, 12, 31));
4841 }
4842
4843 {
4844 auto date = Date(1997, 12, 31);
4845 date.add!"months"(14);
4846 assert(date == Date(1999, 3, 3));
4847 date.add!"months"(-14);
4848 assert(date == Date(1998, 1, 3));
4849 }
4850
4851 {
4852 auto date = Date(1998, 12, 31);
4853 date.add!"months"(14);
4854 assert(date == Date(2000, 3, 2));
4855 date.add!"months"(-14);
4856 assert(date == Date(1999, 1, 2));
4857 }
4858
4859 {
4860 auto date = Date(1999, 12, 31);
4861 date.add!"months"(14);
4862 assert(date == Date(2001, 3, 3));
4863 date.add!"months"(-14);
4864 assert(date == Date(2000, 1, 3));
4865 }
4866
4867 // Test B.C.
4868 {
4869 auto date = Date(-1999, 7, 6);
4870 date.add!"months"(3);
4871 assert(date == Date(-1999, 10, 6));
4872 date.add!"months"(-4);
4873 assert(date == Date(-1999, 6, 6));
4874 }
4875
4876 {
4877 auto date = Date(-1999, 7, 6);
4878 date.add!"months"(6);
4879 assert(date == Date(-1998, 1, 6));
4880 date.add!"months"(-6);
4881 assert(date == Date(-1999, 7, 6));
4882 }
4883
4884 {
4885 auto date = Date(-1999, 7, 6);
4886 date.add!"months"(-27);
4887 assert(date == Date(-2001, 4, 6));
4888 date.add!"months"(28);
4889 assert(date == Date(-1999, 8, 6));
4890 }
4891
4892 {
4893 auto date = Date(-1999, 5, 31);
4894 date.add!"months"(1);
4895 assert(date == Date(-1999, 7, 1));
4896 }
4897
4898 {
4899 auto date = Date(-1999, 5, 31);
4900 date.add!"months"(-1);
4901 assert(date == Date(-1999, 5, 1));
4902 }
4903
4904 {
4905 auto date = Date(-1999, 2, 28);
4906 date.add!"months"(-12);
4907 assert(date == Date(-2000, 2, 28));
4908 }
4909
4910 {
4911 auto date = Date(-2000, 2, 29);
4912 date.add!"months"(-12);
4913 assert(date == Date(-2001, 3, 1));
4914 }
4915
4916 {
4917 auto date = Date(-1999, 7, 31);
4918 date.add!"months"(1);
4919 assert(date == Date(-1999, 8, 31));
4920 date.add!"months"(1);
4921 assert(date == Date(-1999, 10, 1));
4922 }
4923
4924 {
4925 auto date = Date(-1998, 8, 31);
4926 date.add!"months"(13);
4927 assert(date == Date(-1997, 10, 1));
4928 date.add!"months"(-13);
4929 assert(date == Date(-1998, 9, 1));
4930 }
4931
4932 {
4933 auto date = Date(-1997, 12, 31);
4934 date.add!"months"(13);
4935 assert(date == Date(-1995, 1, 31));
4936 date.add!"months"(-13);
4937 assert(date == Date(-1997, 12, 31));
4938 }
4939
4940 {
4941 auto date = Date(-1997, 12, 31);
4942 date.add!"months"(14);
4943 assert(date == Date(-1995, 3, 3));
4944 date.add!"months"(-14);
4945 assert(date == Date(-1996, 1, 3));
4946 }
4947
4948 {
4949 auto date = Date(-2002, 12, 31);
4950 date.add!"months"(14);
4951 assert(date == Date(-2000, 3, 2));
4952 date.add!"months"(-14);
4953 assert(date == Date(-2001, 1, 2));
4954 }
4955
4956 {
4957 auto date = Date(-2001, 12, 31);
4958 date.add!"months"(14);
4959 assert(date == Date(-1999, 3, 3));
4960 date.add!"months"(-14);
4961 assert(date == Date(-2000, 1, 3));
4962 }
4963
4964 // Test Both
4965 {
4966 auto date = Date(1, 1, 1);
4967 date.add!"months"(-1);
4968 assert(date == Date(0, 12, 1));
4969 date.add!"months"(1);
4970 assert(date == Date(1, 1, 1));
4971 }
4972
4973 {
4974 auto date = Date(4, 1, 1);
4975 date.add!"months"(-48);
4976 assert(date == Date(0, 1, 1));
4977 date.add!"months"(48);
4978 assert(date == Date(4, 1, 1));
4979 }
4980
4981 {
4982 auto date = Date(4, 3, 31);
4983 date.add!"months"(-49);
4984 assert(date == Date(0, 3, 2));
4985 date.add!"months"(49);
4986 assert(date == Date(4, 4, 2));
4987 }
4988
4989 {
4990 auto date = Date(4, 3, 31);
4991 date.add!"months"(-85);
4992 assert(date == Date(-3, 3, 3));
4993 date.add!"months"(85);
4994 assert(date == Date(4, 4, 3));
4995 }
4996
4997 {
4998 auto date = Date(-3, 3, 31);
4999 date.add!"months"(85).add!"months"(-83);
5000 assert(date == Date(-3, 6, 1));
5001 }
5002
5003 const cdate = Date(1999, 7, 6);
5004 immutable idate = Date(1999, 7, 6);
5005 static assert(!__traits(compiles, cdate.add!"months"(3)));
5006 static assert(!__traits(compiles, idate.add!"months"(3)));
5007 }
5008
5009 // Test add!"months"() with AllowDayOverflow.no
5010 @safe unittest
5011 {
5012 // Test A.D.
5013 {
5014 auto date = Date(1999, 7, 6);
5015 date.add!"months"(3, AllowDayOverflow.no);
5016 assert(date == Date(1999, 10, 6));
5017 date.add!"months"(-4, AllowDayOverflow.no);
5018 assert(date == Date(1999, 6, 6));
5019 }
5020
5021 {
5022 auto date = Date(1999, 7, 6);
5023 date.add!"months"(6, AllowDayOverflow.no);
5024 assert(date == Date(2000, 1, 6));
5025 date.add!"months"(-6, AllowDayOverflow.no);
5026 assert(date == Date(1999, 7, 6));
5027 }
5028
5029 {
5030 auto date = Date(1999, 7, 6);
5031 date.add!"months"(27, AllowDayOverflow.no);
5032 assert(date == Date(2001, 10, 6));
5033 date.add!"months"(-28, AllowDayOverflow.no);
5034 assert(date == Date(1999, 6, 6));
5035 }
5036
5037 {
5038 auto date = Date(1999, 5, 31);
5039 date.add!"months"(1, AllowDayOverflow.no);
5040 assert(date == Date(1999, 6, 30));
5041 }
5042
5043 {
5044 auto date = Date(1999, 5, 31);
5045 date.add!"months"(-1, AllowDayOverflow.no);
5046 assert(date == Date(1999, 4, 30));
5047 }
5048
5049 {
5050 auto date = Date(1999, 2, 28);
5051 date.add!"months"(12, AllowDayOverflow.no);
5052 assert(date == Date(2000, 2, 28));
5053 }
5054
5055 {
5056 auto date = Date(2000, 2, 29);
5057 date.add!"months"(12, AllowDayOverflow.no);
5058 assert(date == Date(2001, 2, 28));
5059 }
5060
5061 {
5062 auto date = Date(1999, 7, 31);
5063 date.add!"months"(1, AllowDayOverflow.no);
5064 assert(date == Date(1999, 8, 31));
5065 date.add!"months"(1, AllowDayOverflow.no);
5066 assert(date == Date(1999, 9, 30));
5067 }
5068
5069 {
5070 auto date = Date(1998, 8, 31);
5071 date.add!"months"(13, AllowDayOverflow.no);
5072 assert(date == Date(1999, 9, 30));
5073 date.add!"months"(-13, AllowDayOverflow.no);
5074 assert(date == Date(1998, 8, 30));
5075 }
5076
5077 {
5078 auto date = Date(1997, 12, 31);
5079 date.add!"months"(13, AllowDayOverflow.no);
5080 assert(date == Date(1999, 1, 31));
5081 date.add!"months"(-13, AllowDayOverflow.no);
5082 assert(date == Date(1997, 12, 31));
5083 }
5084
5085 {
5086 auto date = Date(1997, 12, 31);
5087 date.add!"months"(14, AllowDayOverflow.no);
5088 assert(date == Date(1999, 2, 28));
5089 date.add!"months"(-14, AllowDayOverflow.no);
5090 assert(date == Date(1997, 12, 28));
5091 }
5092
5093 {
5094 auto date = Date(1998, 12, 31);
5095 date.add!"months"(14, AllowDayOverflow.no);
5096 assert(date == Date(2000, 2, 29));
5097 date.add!"months"(-14, AllowDayOverflow.no);
5098 assert(date == Date(1998, 12, 29));
5099 }
5100
5101 {
5102 auto date = Date(1999, 12, 31);
5103 date.add!"months"(14, AllowDayOverflow.no);
5104 assert(date == Date(2001, 2, 28));
5105 date.add!"months"(-14, AllowDayOverflow.no);
5106 assert(date == Date(1999, 12, 28));
5107 }
5108
5109 // Test B.C.
5110 {
5111 auto date = Date(-1999, 7, 6);
5112 date.add!"months"(3, AllowDayOverflow.no);
5113 assert(date == Date(-1999, 10, 6));
5114 date.add!"months"(-4, AllowDayOverflow.no);
5115 assert(date == Date(-1999, 6, 6));
5116 }
5117
5118 {
5119 auto date = Date(-1999, 7, 6);
5120 date.add!"months"(6, AllowDayOverflow.no);
5121 assert(date == Date(-1998, 1, 6));
5122 date.add!"months"(-6, AllowDayOverflow.no);
5123 assert(date == Date(-1999, 7, 6));
5124 }
5125
5126 {
5127 auto date = Date(-1999, 7, 6);
5128 date.add!"months"(-27, AllowDayOverflow.no);
5129 assert(date == Date(-2001, 4, 6));
5130 date.add!"months"(28, AllowDayOverflow.no);
5131 assert(date == Date(-1999, 8, 6));
5132 }
5133
5134 {
5135 auto date = Date(-1999, 5, 31);
5136 date.add!"months"(1, AllowDayOverflow.no);
5137 assert(date == Date(-1999, 6, 30));
5138 }
5139
5140 {
5141 auto date = Date(-1999, 5, 31);
5142 date.add!"months"(-1, AllowDayOverflow.no);
5143 assert(date == Date(-1999, 4, 30));
5144 }
5145
5146 {
5147 auto date = Date(-1999, 2, 28);
5148 date.add!"months"(-12, AllowDayOverflow.no);
5149 assert(date == Date(-2000, 2, 28));
5150 }
5151
5152 {
5153 auto date = Date(-2000, 2, 29);
5154 date.add!"months"(-12, AllowDayOverflow.no);
5155 assert(date == Date(-2001, 2, 28));
5156 }
5157
5158 {
5159 auto date = Date(-1999, 7, 31);
5160 date.add!"months"(1, AllowDayOverflow.no);
5161 assert(date == Date(-1999, 8, 31));
5162 date.add!"months"(1, AllowDayOverflow.no);
5163 assert(date == Date(-1999, 9, 30));
5164 }
5165
5166 {
5167 auto date = Date(-1998, 8, 31);
5168 date.add!"months"(13, AllowDayOverflow.no);
5169 assert(date == Date(-1997, 9, 30));
5170 date.add!"months"(-13, AllowDayOverflow.no);
5171 assert(date == Date(-1998, 8, 30));
5172 }
5173
5174 {
5175 auto date = Date(-1997, 12, 31);
5176 date.add!"months"(13, AllowDayOverflow.no);
5177 assert(date == Date(-1995, 1, 31));
5178 date.add!"months"(-13, AllowDayOverflow.no);
5179 assert(date == Date(-1997, 12, 31));
5180 }
5181
5182 {
5183 auto date = Date(-1997, 12, 31);
5184 date.add!"months"(14, AllowDayOverflow.no);
5185 assert(date == Date(-1995, 2, 28));
5186 date.add!"months"(-14, AllowDayOverflow.no);
5187 assert(date == Date(-1997, 12, 28));
5188 }
5189
5190 {
5191 auto date = Date(-2002, 12, 31);
5192 date.add!"months"(14, AllowDayOverflow.no);
5193 assert(date == Date(-2000, 2, 29));
5194 date.add!"months"(-14, AllowDayOverflow.no);
5195 assert(date == Date(-2002, 12, 29));
5196 }
5197
5198 {
5199 auto date = Date(-2001, 12, 31);
5200 date.add!"months"(14, AllowDayOverflow.no);
5201 assert(date == Date(-1999, 2, 28));
5202 date.add!"months"(-14, AllowDayOverflow.no);
5203 assert(date == Date(-2001, 12, 28));
5204 }
5205
5206 // Test Both
5207 {
5208 auto date = Date(1, 1, 1);
5209 date.add!"months"(-1, AllowDayOverflow.no);
5210 assert(date == Date(0, 12, 1));
5211 date.add!"months"(1, AllowDayOverflow.no);
5212 assert(date == Date(1, 1, 1));
5213 }
5214
5215 {
5216 auto date = Date(4, 1, 1);
5217 date.add!"months"(-48, AllowDayOverflow.no);
5218 assert(date == Date(0, 1, 1));
5219 date.add!"months"(48, AllowDayOverflow.no);
5220 assert(date == Date(4, 1, 1));
5221 }
5222
5223 {
5224 auto date = Date(4, 3, 31);
5225 date.add!"months"(-49, AllowDayOverflow.no);
5226 assert(date == Date(0, 2, 29));
5227 date.add!"months"(49, AllowDayOverflow.no);
5228 assert(date == Date(4, 3, 29));
5229 }
5230
5231 {
5232 auto date = Date(4, 3, 31);
5233 date.add!"months"(-85, AllowDayOverflow.no);
5234 assert(date == Date(-3, 2, 28));
5235 date.add!"months"(85, AllowDayOverflow.no);
5236 assert(date == Date(4, 3, 28));
5237 }
5238
5239 {
5240 auto date = Date(-3, 3, 31);
5241 date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
5242 assert(date == Date(-3, 5, 30));
5243 }
5244 }
5245
5246
5247 /++
5248 Adds the given number of years or months to this $(LREF Date), mutating
5249 it. A negative number will subtract.
5250
5251 The difference between rolling and adding is that rolling does not
5252 affect larger units. Rolling a $(LREF Date) 12 months gets
5253 the exact same $(LREF Date). However, the days can still be affected due
5254 to the differing number of days in each month.
5255
5256 Because there are no units larger than years, there is no difference
5257 between adding and rolling years.
5258
5259 Params:
5260 units = The type of units to add ("years" or "months").
5261 value = The number of months or years to add to this
5262 $(LREF Date).
5263 allowOverflow = Whether the day should be allowed to overflow,
5264 causing the month to increment.
5265
5266 Returns:
5267 A reference to the `Date` (`this`).
5268 +/
5269 @safe pure nothrow @nogc
5270 ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5271 if (units == "years")
5272 {
5273 return add!"years"(value, allowOverflow);
5274 }
5275
5276 ///
5277 @safe unittest
5278 {
5279 auto d1 = Date(2010, 1, 1);
5280 d1.roll!"months"(1);
5281 assert(d1 == Date(2010, 2, 1));
5282
5283 auto d2 = Date(2010, 1, 1);
5284 d2.roll!"months"(-1);
5285 assert(d2 == Date(2010, 12, 1));
5286
5287 auto d3 = Date(1999, 1, 29);
5288 d3.roll!"months"(1);
5289 assert(d3 == Date(1999, 3, 1));
5290
5291 auto d4 = Date(1999, 1, 29);
5292 d4.roll!"months"(1, AllowDayOverflow.no);
5293 assert(d4 == Date(1999, 2, 28));
5294
5295 auto d5 = Date(2000, 2, 29);
5296 d5.roll!"years"(1);
5297 assert(d5 == Date(2001, 3, 1));
5298
5299 auto d6 = Date(2000, 2, 29);
5300 d6.roll!"years"(1, AllowDayOverflow.no);
5301 assert(d6 == Date(2001, 2, 28));
5302 }
5303
5304 @safe unittest
5305 {
5306 const cdate = Date(1999, 7, 6);
5307 immutable idate = Date(1999, 7, 6);
5308 static assert(!__traits(compiles, cdate.roll!"years"(3)));
5309 static assert(!__traits(compiles, idate.rolYears(3)));
5310 }
5311
5312
5313 // Shares documentation with "years" version.
5314 @safe pure nothrow @nogc
5315 ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5316 if (units == "months")
5317 {
5318 months %= 12;
5319 auto newMonth = _month + months;
5320
5321 if (months < 0)
5322 {
5323 if (newMonth < 1)
5324 newMonth += 12;
5325 }
5326 else
5327 {
5328 if (newMonth > 12)
5329 newMonth -= 12;
5330 }
5331
5332 _month = cast(Month) newMonth;
5333
5334 immutable currMaxDay = maxDay(_year, _month);
5335 immutable overflow = _day - currMaxDay;
5336
5337 if (overflow > 0)
5338 {
5339 if (allowOverflow == AllowDayOverflow.yes)
5340 {
5341 ++_month;
5342 _day = cast(ubyte) overflow;
5343 }
5344 else
5345 _day = cast(ubyte) currMaxDay;
5346 }
5347
5348 return this;
5349 }
5350
5351 // Test roll!"months"() with AllowDayOverflow.yes
5352 @safe unittest
5353 {
5354 // Test A.D.
5355 {
5356 auto date = Date(1999, 7, 6);
5357 date.roll!"months"(3);
5358 assert(date == Date(1999, 10, 6));
5359 date.roll!"months"(-4);
5360 assert(date == Date(1999, 6, 6));
5361 }
5362
5363 {
5364 auto date = Date(1999, 7, 6);
5365 date.roll!"months"(6);
5366 assert(date == Date(1999, 1, 6));
5367 date.roll!"months"(-6);
5368 assert(date == Date(1999, 7, 6));
5369 }
5370
5371 {
5372 auto date = Date(1999, 7, 6);
5373 date.roll!"months"(27);
5374 assert(date == Date(1999, 10, 6));
5375 date.roll!"months"(-28);
5376 assert(date == Date(1999, 6, 6));
5377 }
5378
5379 {
5380 auto date = Date(1999, 5, 31);
5381 date.roll!"months"(1);
5382 assert(date == Date(1999, 7, 1));
5383 }
5384
5385 {
5386 auto date = Date(1999, 5, 31);
5387 date.roll!"months"(-1);
5388 assert(date == Date(1999, 5, 1));
5389 }
5390
5391 {
5392 auto date = Date(1999, 2, 28);
5393 date.roll!"months"(12);
5394 assert(date == Date(1999, 2, 28));
5395 }
5396
5397 {
5398 auto date = Date(2000, 2, 29);
5399 date.roll!"months"(12);
5400 assert(date == Date(2000, 2, 29));
5401 }
5402
5403 {
5404 auto date = Date(1999, 7, 31);
5405 date.roll!"months"(1);
5406 assert(date == Date(1999, 8, 31));
5407 date.roll!"months"(1);
5408 assert(date == Date(1999, 10, 1));
5409 }
5410
5411 {
5412 auto date = Date(1998, 8, 31);
5413 date.roll!"months"(13);
5414 assert(date == Date(1998, 10, 1));
5415 date.roll!"months"(-13);
5416 assert(date == Date(1998, 9, 1));
5417 }
5418
5419 {
5420 auto date = Date(1997, 12, 31);
5421 date.roll!"months"(13);
5422 assert(date == Date(1997, 1, 31));
5423 date.roll!"months"(-13);
5424 assert(date == Date(1997, 12, 31));
5425 }
5426
5427 {
5428 auto date = Date(1997, 12, 31);
5429 date.roll!"months"(14);
5430 assert(date == Date(1997, 3, 3));
5431 date.roll!"months"(-14);
5432 assert(date == Date(1997, 1, 3));
5433 }
5434
5435 {
5436 auto date = Date(1998, 12, 31);
5437 date.roll!"months"(14);
5438 assert(date == Date(1998, 3, 3));
5439 date.roll!"months"(-14);
5440 assert(date == Date(1998, 1, 3));
5441 }
5442
5443 {
5444 auto date = Date(1999, 12, 31);
5445 date.roll!"months"(14);
5446 assert(date == Date(1999, 3, 3));
5447 date.roll!"months"(-14);
5448 assert(date == Date(1999, 1, 3));
5449 }
5450
5451 // Test B.C.
5452 {
5453 auto date = Date(-1999, 7, 6);
5454 date.roll!"months"(3);
5455 assert(date == Date(-1999, 10, 6));
5456 date.roll!"months"(-4);
5457 assert(date == Date(-1999, 6, 6));
5458 }
5459
5460 {
5461 auto date = Date(-1999, 7, 6);
5462 date.roll!"months"(6);
5463 assert(date == Date(-1999, 1, 6));
5464 date.roll!"months"(-6);
5465 assert(date == Date(-1999, 7, 6));
5466 }
5467
5468 {
5469 auto date = Date(-1999, 7, 6);
5470 date.roll!"months"(-27);
5471 assert(date == Date(-1999, 4, 6));
5472 date.roll!"months"(28);
5473 assert(date == Date(-1999, 8, 6));
5474 }
5475
5476 {
5477 auto date = Date(-1999, 5, 31);
5478 date.roll!"months"(1);
5479 assert(date == Date(-1999, 7, 1));
5480 }
5481
5482 {
5483 auto date = Date(-1999, 5, 31);
5484 date.roll!"months"(-1);
5485 assert(date == Date(-1999, 5, 1));
5486 }
5487
5488 {
5489 auto date = Date(-1999, 2, 28);
5490 date.roll!"months"(-12);
5491 assert(date == Date(-1999, 2, 28));
5492 }
5493
5494 {
5495 auto date = Date(-2000, 2, 29);
5496 date.roll!"months"(-12);
5497 assert(date == Date(-2000, 2, 29));
5498 }
5499
5500 {
5501 auto date = Date(-1999, 7, 31);
5502 date.roll!"months"(1);
5503 assert(date == Date(-1999, 8, 31));
5504 date.roll!"months"(1);
5505 assert(date == Date(-1999, 10, 1));
5506 }
5507
5508 {
5509 auto date = Date(-1998, 8, 31);
5510 date.roll!"months"(13);
5511 assert(date == Date(-1998, 10, 1));
5512 date.roll!"months"(-13);
5513 assert(date == Date(-1998, 9, 1));
5514 }
5515
5516 {
5517 auto date = Date(-1997, 12, 31);
5518 date.roll!"months"(13);
5519 assert(date == Date(-1997, 1, 31));
5520 date.roll!"months"(-13);
5521 assert(date == Date(-1997, 12, 31));
5522 }
5523
5524 {
5525 auto date = Date(-1997, 12, 31);
5526 date.roll!"months"(14);
5527 assert(date == Date(-1997, 3, 3));
5528 date.roll!"months"(-14);
5529 assert(date == Date(-1997, 1, 3));
5530 }
5531
5532 {
5533 auto date = Date(-2002, 12, 31);
5534 date.roll!"months"(14);
5535 assert(date == Date(-2002, 3, 3));
5536 date.roll!"months"(-14);
5537 assert(date == Date(-2002, 1, 3));
5538 }
5539
5540 {
5541 auto date = Date(-2001, 12, 31);
5542 date.roll!"months"(14);
5543 assert(date == Date(-2001, 3, 3));
5544 date.roll!"months"(-14);
5545 assert(date == Date(-2001, 1, 3));
5546 }
5547
5548 // Test Both
5549 {
5550 auto date = Date(1, 1, 1);
5551 date.roll!"months"(-1);
5552 assert(date == Date(1, 12, 1));
5553 date.roll!"months"(1);
5554 assert(date == Date(1, 1, 1));
5555 }
5556
5557 {
5558 auto date = Date(4, 1, 1);
5559 date.roll!"months"(-48);
5560 assert(date == Date(4, 1, 1));
5561 date.roll!"months"(48);
5562 assert(date == Date(4, 1, 1));
5563 }
5564
5565 {
5566 auto date = Date(4, 3, 31);
5567 date.roll!"months"(-49);
5568 assert(date == Date(4, 3, 2));
5569 date.roll!"months"(49);
5570 assert(date == Date(4, 4, 2));
5571 }
5572
5573 {
5574 auto date = Date(4, 3, 31);
5575 date.roll!"months"(-85);
5576 assert(date == Date(4, 3, 2));
5577 date.roll!"months"(85);
5578 assert(date == Date(4, 4, 2));
5579 }
5580
5581 {
5582 auto date = Date(-1, 1, 1);
5583 date.roll!"months"(-1);
5584 assert(date == Date(-1, 12, 1));
5585 date.roll!"months"(1);
5586 assert(date == Date(-1, 1, 1));
5587 }
5588
5589 {
5590 auto date = Date(-4, 1, 1);
5591 date.roll!"months"(-48);
5592 assert(date == Date(-4, 1, 1));
5593 date.roll!"months"(48);
5594 assert(date == Date(-4, 1, 1));
5595 }
5596
5597 {
5598 auto date = Date(-4, 3, 31);
5599 date.roll!"months"(-49);
5600 assert(date == Date(-4, 3, 2));
5601 date.roll!"months"(49);
5602 assert(date == Date(-4, 4, 2));
5603 }
5604
5605 {
5606 auto date = Date(-4, 3, 31);
5607 date.roll!"months"(-85);
5608 assert(date == Date(-4, 3, 2));
5609 date.roll!"months"(85);
5610 assert(date == Date(-4, 4, 2));
5611 }
5612
5613 {
5614 auto date = Date(-3, 3, 31);
5615 date.roll!"months"(85).roll!"months"(-83);
5616 assert(date == Date(-3, 6, 1));
5617 }
5618
5619 const cdate = Date(1999, 7, 6);
5620 immutable idate = Date(1999, 7, 6);
5621 static assert(!__traits(compiles, cdate.roll!"months"(3)));
5622 static assert(!__traits(compiles, idate.roll!"months"(3)));
5623 }
5624
5625 // Test roll!"months"() with AllowDayOverflow.no
5626 @safe unittest
5627 {
5628 // Test A.D.
5629 {
5630 auto date = Date(1999, 7, 6);
5631 date.roll!"months"(3, AllowDayOverflow.no);
5632 assert(date == Date(1999, 10, 6));
5633 date.roll!"months"(-4, AllowDayOverflow.no);
5634 assert(date == Date(1999, 6, 6));
5635 }
5636
5637 {
5638 auto date = Date(1999, 7, 6);
5639 date.roll!"months"(6, AllowDayOverflow.no);
5640 assert(date == Date(1999, 1, 6));
5641 date.roll!"months"(-6, AllowDayOverflow.no);
5642 assert(date == Date(1999, 7, 6));
5643 }
5644
5645 {
5646 auto date = Date(1999, 7, 6);
5647 date.roll!"months"(27, AllowDayOverflow.no);
5648 assert(date == Date(1999, 10, 6));
5649 date.roll!"months"(-28, AllowDayOverflow.no);
5650 assert(date == Date(1999, 6, 6));
5651 }
5652
5653 {
5654 auto date = Date(1999, 5, 31);
5655 date.roll!"months"(1, AllowDayOverflow.no);
5656 assert(date == Date(1999, 6, 30));
5657 }
5658
5659 {
5660 auto date = Date(1999, 5, 31);
5661 date.roll!"months"(-1, AllowDayOverflow.no);
5662 assert(date == Date(1999, 4, 30));
5663 }
5664
5665 {
5666 auto date = Date(1999, 2, 28);
5667 date.roll!"months"(12, AllowDayOverflow.no);
5668 assert(date == Date(1999, 2, 28));
5669 }
5670
5671 {
5672 auto date = Date(2000, 2, 29);
5673 date.roll!"months"(12, AllowDayOverflow.no);
5674 assert(date == Date(2000, 2, 29));
5675 }
5676
5677 {
5678 auto date = Date(1999, 7, 31);
5679 date.roll!"months"(1, AllowDayOverflow.no);
5680 assert(date == Date(1999, 8, 31));
5681 date.roll!"months"(1, AllowDayOverflow.no);
5682 assert(date == Date(1999, 9, 30));
5683 }
5684
5685 {
5686 auto date = Date(1998, 8, 31);
5687 date.roll!"months"(13, AllowDayOverflow.no);
5688 assert(date == Date(1998, 9, 30));
5689 date.roll!"months"(-13, AllowDayOverflow.no);
5690 assert(date == Date(1998, 8, 30));
5691 }
5692
5693 {
5694 auto date = Date(1997, 12, 31);
5695 date.roll!"months"(13, AllowDayOverflow.no);
5696 assert(date == Date(1997, 1, 31));
5697 date.roll!"months"(-13, AllowDayOverflow.no);
5698 assert(date == Date(1997, 12, 31));
5699 }
5700
5701 {
5702 auto date = Date(1997, 12, 31);
5703 date.roll!"months"(14, AllowDayOverflow.no);
5704 assert(date == Date(1997, 2, 28));
5705 date.roll!"months"(-14, AllowDayOverflow.no);
5706 assert(date == Date(1997, 12, 28));
5707 }
5708
5709 {
5710 auto date = Date(1998, 12, 31);
5711 date.roll!"months"(14, AllowDayOverflow.no);
5712 assert(date == Date(1998, 2, 28));
5713 date.roll!"months"(-14, AllowDayOverflow.no);
5714 assert(date == Date(1998, 12, 28));
5715 }
5716
5717 {
5718 auto date = Date(1999, 12, 31);
5719 date.roll!"months"(14, AllowDayOverflow.no);
5720 assert(date == Date(1999, 2, 28));
5721 date.roll!"months"(-14, AllowDayOverflow.no);
5722 assert(date == Date(1999, 12, 28));
5723 }
5724
5725 // Test B.C.
5726 {
5727 auto date = Date(-1999, 7, 6);
5728 date.roll!"months"(3, AllowDayOverflow.no);
5729 assert(date == Date(-1999, 10, 6));
5730 date.roll!"months"(-4, AllowDayOverflow.no);
5731 assert(date == Date(-1999, 6, 6));
5732 }
5733
5734 {
5735 auto date = Date(-1999, 7, 6);
5736 date.roll!"months"(6, AllowDayOverflow.no);
5737 assert(date == Date(-1999, 1, 6));
5738 date.roll!"months"(-6, AllowDayOverflow.no);
5739 assert(date == Date(-1999, 7, 6));
5740 }
5741
5742 {
5743 auto date = Date(-1999, 7, 6);
5744 date.roll!"months"(-27, AllowDayOverflow.no);
5745 assert(date == Date(-1999, 4, 6));
5746 date.roll!"months"(28, AllowDayOverflow.no);
5747 assert(date == Date(-1999, 8, 6));
5748 }
5749
5750 {
5751 auto date = Date(-1999, 5, 31);
5752 date.roll!"months"(1, AllowDayOverflow.no);
5753 assert(date == Date(-1999, 6, 30));
5754 }
5755
5756 {
5757 auto date = Date(-1999, 5, 31);
5758 date.roll!"months"(-1, AllowDayOverflow.no);
5759 assert(date == Date(-1999, 4, 30));
5760 }
5761
5762 {
5763 auto date = Date(-1999, 2, 28);
5764 date.roll!"months"(-12, AllowDayOverflow.no);
5765 assert(date == Date(-1999, 2, 28));
5766 }
5767
5768 {
5769 auto date = Date(-2000, 2, 29);
5770 date.roll!"months"(-12, AllowDayOverflow.no);
5771 assert(date == Date(-2000, 2, 29));
5772 }
5773
5774 {
5775 auto date = Date(-1999, 7, 31);
5776 date.roll!"months"(1, AllowDayOverflow.no);
5777 assert(date == Date(-1999, 8, 31));
5778 date.roll!"months"(1, AllowDayOverflow.no);
5779 assert(date == Date(-1999, 9, 30));
5780 }
5781
5782 {
5783 auto date = Date(-1998, 8, 31);
5784 date.roll!"months"(13, AllowDayOverflow.no);
5785 assert(date == Date(-1998, 9, 30));
5786 date.roll!"months"(-13, AllowDayOverflow.no);
5787 assert(date == Date(-1998, 8, 30));
5788 }
5789
5790 {
5791 auto date = Date(-1997, 12, 31);
5792 date.roll!"months"(13, AllowDayOverflow.no);
5793 assert(date == Date(-1997, 1, 31));
5794 date.roll!"months"(-13, AllowDayOverflow.no);
5795 assert(date == Date(-1997, 12, 31));
5796 }
5797
5798 {
5799 auto date = Date(-1997, 12, 31);
5800 date.roll!"months"(14, AllowDayOverflow.no);
5801 assert(date == Date(-1997, 2, 28));
5802 date.roll!"months"(-14, AllowDayOverflow.no);
5803 assert(date == Date(-1997, 12, 28));
5804 }
5805
5806 {
5807 auto date = Date(-2002, 12, 31);
5808 date.roll!"months"(14, AllowDayOverflow.no);
5809 assert(date == Date(-2002, 2, 28));
5810 date.roll!"months"(-14, AllowDayOverflow.no);
5811 assert(date == Date(-2002, 12, 28));
5812 }
5813
5814 {
5815 auto date = Date(-2001, 12, 31);
5816 date.roll!"months"(14, AllowDayOverflow.no);
5817 assert(date == Date(-2001, 2, 28));
5818 date.roll!"months"(-14, AllowDayOverflow.no);
5819 assert(date == Date(-2001, 12, 28));
5820 }
5821
5822 // Test Both
5823 {
5824 auto date = Date(1, 1, 1);
5825 date.roll!"months"(-1, AllowDayOverflow.no);
5826 assert(date == Date(1, 12, 1));
5827 date.roll!"months"(1, AllowDayOverflow.no);
5828 assert(date == Date(1, 1, 1));
5829 }
5830
5831 {
5832 auto date = Date(4, 1, 1);
5833 date.roll!"months"(-48, AllowDayOverflow.no);
5834 assert(date == Date(4, 1, 1));
5835 date.roll!"months"(48, AllowDayOverflow.no);
5836 assert(date == Date(4, 1, 1));
5837 }
5838
5839 {
5840 auto date = Date(4, 3, 31);
5841 date.roll!"months"(-49, AllowDayOverflow.no);
5842 assert(date == Date(4, 2, 29));
5843 date.roll!"months"(49, AllowDayOverflow.no);
5844 assert(date == Date(4, 3, 29));
5845 }
5846
5847 {
5848 auto date = Date(4, 3, 31);
5849 date.roll!"months"(-85, AllowDayOverflow.no);
5850 assert(date == Date(4, 2, 29));
5851 date.roll!"months"(85, AllowDayOverflow.no);
5852 assert(date == Date(4, 3, 29));
5853 }
5854
5855 {
5856 auto date = Date(-1, 1, 1);
5857 date.roll!"months"(-1, AllowDayOverflow.no);
5858 assert(date == Date(-1, 12, 1));
5859 date.roll!"months"(1, AllowDayOverflow.no);
5860 assert(date == Date(-1, 1, 1));
5861 }
5862
5863 {
5864 auto date = Date(-4, 1, 1);
5865 date.roll!"months"(-48, AllowDayOverflow.no);
5866 assert(date == Date(-4, 1, 1));
5867 date.roll!"months"(48, AllowDayOverflow.no);
5868 assert(date == Date(-4, 1, 1));
5869 }
5870
5871 {
5872 auto date = Date(-4, 3, 31);
5873 date.roll!"months"(-49, AllowDayOverflow.no);
5874 assert(date == Date(-4, 2, 29));
5875 date.roll!"months"(49, AllowDayOverflow.no);
5876 assert(date == Date(-4, 3, 29));
5877 }
5878
5879 {
5880 auto date = Date(-4, 3, 31);
5881 date.roll!"months"(-85, AllowDayOverflow.no);
5882 assert(date == Date(-4, 2, 29));
5883 date.roll!"months"(85, AllowDayOverflow.no);
5884 assert(date == Date(-4, 3, 29));
5885 }
5886
5887 {
5888 auto date = Date(-3, 3, 31);
5889 date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
5890 assert(date == Date(-3, 5, 30));
5891 }
5892 }
5893
5894
5895 /++
5896 Adds the given number of units to this $(LREF Date), mutating it. A
5897 negative number will subtract.
5898
5899 The difference between rolling and adding is that rolling does not
5900 affect larger units. For instance, rolling a $(LREF Date) one
5901 year's worth of days gets the exact same $(LREF Date).
5902
5903 The only accepted units are `"days"`.
5904
5905 Params:
5906 units = The units to add. Must be `"days"`.
5907 days = The number of days to add to this $(LREF Date).
5908
5909 Returns:
5910 A reference to the `Date` (`this`).
5911 +/
5912 ref Date roll(string units)(long days) @safe pure nothrow @nogc
5913 if (units == "days")
5914 {
5915 immutable limit = maxDay(_year, _month);
5916 days %= limit;
5917 auto newDay = _day + days;
5918
5919 if (days < 0)
5920 {
5921 if (newDay < 1)
5922 newDay += limit;
5923 }
5924 else if (newDay > limit)
5925 newDay -= limit;
5926
5927 _day = cast(ubyte) newDay;
5928 return this;
5929 }
5930
5931 ///
5932 @safe unittest
5933 {
5934 auto d = Date(2010, 1, 1);
5935 d.roll!"days"(1);
5936 assert(d == Date(2010, 1, 2));
5937 d.roll!"days"(365);
5938 assert(d == Date(2010, 1, 26));
5939 d.roll!"days"(-32);
5940 assert(d == Date(2010, 1, 25));
5941 }
5942
5943 @safe unittest
5944 {
5945 // Test A.D.
5946 {
5947 auto date = Date(1999, 2, 28);
5948 date.roll!"days"(1);
5949 assert(date == Date(1999, 2, 1));
5950 date.roll!"days"(-1);
5951 assert(date == Date(1999, 2, 28));
5952 }
5953
5954 {
5955 auto date = Date(2000, 2, 28);
5956 date.roll!"days"(1);
5957 assert(date == Date(2000, 2, 29));
5958 date.roll!"days"(1);
5959 assert(date == Date(2000, 2, 1));
5960 date.roll!"days"(-1);
5961 assert(date == Date(2000, 2, 29));
5962 }
5963
5964 {
5965 auto date = Date(1999, 6, 30);
5966 date.roll!"days"(1);
5967 assert(date == Date(1999, 6, 1));
5968 date.roll!"days"(-1);
5969 assert(date == Date(1999, 6, 30));
5970 }
5971
5972 {
5973 auto date = Date(1999, 7, 31);
5974 date.roll!"days"(1);
5975 assert(date == Date(1999, 7, 1));
5976 date.roll!"days"(-1);
5977 assert(date == Date(1999, 7, 31));
5978 }
5979
5980 {
5981 auto date = Date(1999, 1, 1);
5982 date.roll!"days"(-1);
5983 assert(date == Date(1999, 1, 31));
5984 date.roll!"days"(1);
5985 assert(date == Date(1999, 1, 1));
5986 }
5987
5988 {
5989 auto date = Date(1999, 7, 6);
5990 date.roll!"days"(9);
5991 assert(date == Date(1999, 7, 15));
5992 date.roll!"days"(-11);
5993 assert(date == Date(1999, 7, 4));
5994 date.roll!"days"(30);
5995 assert(date == Date(1999, 7, 3));
5996 date.roll!"days"(-3);
5997 assert(date == Date(1999, 7, 31));
5998 }
5999
6000 {
6001 auto date = Date(1999, 7, 6);
6002 date.roll!"days"(365);
6003 assert(date == Date(1999, 7, 30));
6004 date.roll!"days"(-365);
6005 assert(date == Date(1999, 7, 6));
6006 date.roll!"days"(366);
6007 assert(date == Date(1999, 7, 31));
6008 date.roll!"days"(730);
6009 assert(date == Date(1999, 7, 17));
6010 date.roll!"days"(-1096);
6011 assert(date == Date(1999, 7, 6));
6012 }
6013
6014 {
6015 auto date = Date(1999, 2, 6);
6016 date.roll!"days"(365);
6017 assert(date == Date(1999, 2, 7));
6018 date.roll!"days"(-365);
6019 assert(date == Date(1999, 2, 6));
6020 date.roll!"days"(366);
6021 assert(date == Date(1999, 2, 8));
6022 date.roll!"days"(730);
6023 assert(date == Date(1999, 2, 10));
6024 date.roll!"days"(-1096);
6025 assert(date == Date(1999, 2, 6));
6026 }
6027
6028 // Test B.C.
6029 {
6030 auto date = Date(-1999, 2, 28);
6031 date.roll!"days"(1);
6032 assert(date == Date(-1999, 2, 1));
6033 date.roll!"days"(-1);
6034 assert(date == Date(-1999, 2, 28));
6035 }
6036
6037 {
6038 auto date = Date(-2000, 2, 28);
6039 date.roll!"days"(1);
6040 assert(date == Date(-2000, 2, 29));
6041 date.roll!"days"(1);
6042 assert(date == Date(-2000, 2, 1));
6043 date.roll!"days"(-1);
6044 assert(date == Date(-2000, 2, 29));
6045 }
6046
6047 {
6048 auto date = Date(-1999, 6, 30);
6049 date.roll!"days"(1);
6050 assert(date == Date(-1999, 6, 1));
6051 date.roll!"days"(-1);
6052 assert(date == Date(-1999, 6, 30));
6053 }
6054
6055 {
6056 auto date = Date(-1999, 7, 31);
6057 date.roll!"days"(1);
6058 assert(date == Date(-1999, 7, 1));
6059 date.roll!"days"(-1);
6060 assert(date == Date(-1999, 7, 31));
6061 }
6062
6063 {
6064 auto date = Date(-1999, 1, 1);
6065 date.roll!"days"(-1);
6066 assert(date == Date(-1999, 1, 31));
6067 date.roll!"days"(1);
6068 assert(date == Date(-1999, 1, 1));
6069 }
6070
6071 {
6072 auto date = Date(-1999, 7, 6);
6073 date.roll!"days"(9);
6074 assert(date == Date(-1999, 7, 15));
6075 date.roll!"days"(-11);
6076 assert(date == Date(-1999, 7, 4));
6077 date.roll!"days"(30);
6078 assert(date == Date(-1999, 7, 3));
6079 date.roll!"days"(-3);
6080 assert(date == Date(-1999, 7, 31));
6081 }
6082
6083 {
6084 auto date = Date(-1999, 7, 6);
6085 date.roll!"days"(365);
6086 assert(date == Date(-1999, 7, 30));
6087 date.roll!"days"(-365);
6088 assert(date == Date(-1999, 7, 6));
6089 date.roll!"days"(366);
6090 assert(date == Date(-1999, 7, 31));
6091 date.roll!"days"(730);
6092 assert(date == Date(-1999, 7, 17));
6093 date.roll!"days"(-1096);
6094 assert(date == Date(-1999, 7, 6));
6095 }
6096
6097 // Test Both
6098 {
6099 auto date = Date(1, 7, 6);
6100 date.roll!"days"(-365);
6101 assert(date == Date(1, 7, 13));
6102 date.roll!"days"(365);
6103 assert(date == Date(1, 7, 6));
6104 date.roll!"days"(-731);
6105 assert(date == Date(1, 7, 19));
6106 date.roll!"days"(730);
6107 assert(date == Date(1, 7, 5));
6108 }
6109
6110 {
6111 auto date = Date(0, 7, 6);
6112 date.roll!"days"(-365);
6113 assert(date == Date(0, 7, 13));
6114 date.roll!"days"(365);
6115 assert(date == Date(0, 7, 6));
6116 date.roll!"days"(-731);
6117 assert(date == Date(0, 7, 19));
6118 date.roll!"days"(730);
6119 assert(date == Date(0, 7, 5));
6120 }
6121
6122 {
6123 auto date = Date(0, 7, 6);
6124 date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
6125 assert(date == Date(0, 7, 8));
6126 }
6127
6128 const cdate = Date(1999, 7, 6);
6129 immutable idate = Date(1999, 7, 6);
6130 static assert(!__traits(compiles, cdate.roll!"days"(12)));
6131 static assert(!__traits(compiles, idate.roll!"days"(12)));
6132 }
6133
6134 import core.time : Duration;
6135 /++
6136 Gives the result of adding or subtracting a $(REF Duration, core,time)
6137 from
6138
6139 The legal types of arithmetic for $(LREF Date) using this operator are
6140
6141 $(BOOKTABLE,
6142 $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6143 $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6144 )
6145
6146 Params:
6147 duration = The $(REF Duration, core,time) to add to or subtract from
6148 this $(LREF Date).
6149 +/
6150 Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
6151 if (op == "+" || op == "-")
6152 {
6153 Date retval = this;
6154 immutable days = duration.total!"days";
6155 mixin("return retval._addDays(" ~ op ~ "days);");
6156 }
6157
6158 ///
6159 @safe unittest
6160 {
6161 import core.time : days;
6162
6163 assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
6164 assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
6165
6166 assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
6167 assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
6168 }
6169
6170 @safe unittest
6171 {
6172 auto date = Date(1999, 7, 6);
6173
6174 import core.time : dur;
6175 assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
6176 assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
6177 assert(date + dur!"days"(7) == Date(1999, 7, 13));
6178 assert(date + dur!"days"(-7) == Date(1999, 6, 29));
6179
6180 assert(date + dur!"hours"(24) == Date(1999, 7, 7));
6181 assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
6182 assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
6183 assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
6184 assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
6185 assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6186 assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6187 assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6188 assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6189 assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6190 assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6191 assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6192
6193 assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
6194 assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
6195 assert(date - dur!"days"(-7) == Date(1999, 7, 13));
6196 assert(date - dur!"days"(7) == Date(1999, 6, 29));
6197
6198 assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
6199 assert(date - dur!"hours"(24) == Date(1999, 7, 5));
6200 assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
6201 assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
6202 assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6203 assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
6204 assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6205 assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6206 assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6207 assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6208 assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6209 assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6210
6211 auto duration = dur!"days"(12);
6212 const cdate = Date(1999, 7, 6);
6213 immutable idate = Date(1999, 7, 6);
6214 assert(date + duration == Date(1999, 7, 18));
6215 assert(cdate + duration == Date(1999, 7, 18));
6216 assert(idate + duration == Date(1999, 7, 18));
6217
6218 assert(date - duration == Date(1999, 6, 24));
6219 assert(cdate - duration == Date(1999, 6, 24));
6220 assert(idate - duration == Date(1999, 6, 24));
6221 }
6222
6223
6224 /++
6225 Gives the result of adding or subtracting a $(REF Duration, core,time)
6226 from this $(LREF Date), as well as assigning the result to this
6227 $(LREF Date).
6228
6229 The legal types of arithmetic for $(LREF Date) using this operator are
6230
6231 $(BOOKTABLE,
6232 $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6233 $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6234 )
6235
6236 Params:
6237 duration = The $(REF Duration, core,time) to add to or subtract from
6238 this $(LREF Date).
6239 +/
6240 ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
6241 if (op == "+" || op == "-")
6242 {
6243 immutable days = duration.total!"days";
6244 mixin("return _addDays(" ~ op ~ "days);");
6245 }
6246
6247 @safe unittest
6248 {
6249 import core.time : dur;
6250 assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
6251 assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
6252 assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
6253 assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
6254
6255 assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
6256 assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
6257 assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
6258 assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
6259 assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
6260 assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6261 assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6262 assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6263 assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6264 assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6265 assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6266 assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6267
6268 assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
6269 assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
6270 assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
6271 assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
6272
6273 assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
6274 assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
6275 assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
6276 assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
6277 assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6278 assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
6279 assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6280 assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6281 assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6282 assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6283 assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6284 assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6285
6286 {
6287 auto date = Date(0, 1, 31);
6288 (date += dur!"days"(507)) += dur!"days"(-2);
6289 assert(date == Date(1, 6, 19));
6290 }
6291
6292 auto duration = dur!"days"(12);
6293 auto date = Date(1999, 7, 6);
6294 const cdate = Date(1999, 7, 6);
6295 immutable idate = Date(1999, 7, 6);
6296 date += duration;
6297 static assert(!__traits(compiles, cdate += duration));
6298 static assert(!__traits(compiles, idate += duration));
6299
6300 date -= duration;
6301 static assert(!__traits(compiles, cdate -= duration));
6302 static assert(!__traits(compiles, idate -= duration));
6303 }
6304
6305
6306 /++
6307 Gives the difference between two $(LREF Date)s.
6308
6309 The legal types of arithmetic for $(LREF Date) using this operator are
6310
6311 $(BOOKTABLE,
6312 $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
6313 )
6314 +/
6315 Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
6316 if (op == "-")
6317 {
6318 import core.time : dur;
6319 return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
6320 }
6321
6322 @safe unittest
6323 {
6324 auto date = Date(1999, 7, 6);
6325
6326 import core.time : dur;
6327 assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
6328 assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
6329 assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
6330 assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
6331 assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
6332 assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
6333
6334 const cdate = Date(1999, 7, 6);
6335 immutable idate = Date(1999, 7, 6);
6336 assert(date - date == Duration.zero);
6337 assert(cdate - date == Duration.zero);
6338 assert(idate - date == Duration.zero);
6339
6340 assert(date - cdate == Duration.zero);
6341 assert(cdate - cdate == Duration.zero);
6342 assert(idate - cdate == Duration.zero);
6343
6344 assert(date - idate == Duration.zero);
6345 assert(cdate - idate == Duration.zero);
6346 assert(idate - idate == Duration.zero);
6347 }
6348
6349
6350 /++
6351 Returns the difference between the two $(LREF Date)s in months.
6352
6353 To get the difference in years, subtract the year property
6354 of two $(LREF Date)s. To get the difference in days or weeks,
6355 subtract the $(LREF Date)s themselves and use the
6356 $(REF Duration, core,time) that results. Because converting between
6357 months and smaller units requires a specific date (which
6358 $(REF Duration, core,time)s don't have), getting the difference in
6359 months requires some math using both the year and month properties, so
6360 this is a convenience function for getting the difference in months.
6361
6362 Note that the number of days in the months or how far into the month
6363 either $(LREF Date) is is irrelevant. It is the difference in the month
6364 property combined with the difference in years * 12. So, for instance,
6365 December 31st and January 1st are one month apart just as December 1st
6366 and January 31st are one month apart.
6367
6368 Params:
6369 rhs = The $(LREF Date) to subtract from this one.
6370 +/
6371 int diffMonths(Date rhs) const @safe pure nothrow @nogc
6372 {
6373 immutable yearDiff = _year - rhs._year;
6374 immutable monthDiff = _month - rhs._month;
6375
6376 return yearDiff * 12 + monthDiff;
6377 }
6378
6379 ///
6380 @safe unittest
6381 {
6382 assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
6383 assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
6384 assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
6385 assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
6386 }
6387
6388 @safe unittest
6389 {
6390 auto date = Date(1999, 7, 6);
6391
6392 // Test A.D.
6393 assert(date.diffMonths(Date(1998, 6, 5)) == 13);
6394 assert(date.diffMonths(Date(1998, 7, 5)) == 12);
6395 assert(date.diffMonths(Date(1998, 8, 5)) == 11);
6396 assert(date.diffMonths(Date(1998, 9, 5)) == 10);
6397 assert(date.diffMonths(Date(1998, 10, 5)) == 9);
6398 assert(date.diffMonths(Date(1998, 11, 5)) == 8);
6399 assert(date.diffMonths(Date(1998, 12, 5)) == 7);
6400 assert(date.diffMonths(Date(1999, 1, 5)) == 6);
6401 assert(date.diffMonths(Date(1999, 2, 6)) == 5);
6402 assert(date.diffMonths(Date(1999, 3, 6)) == 4);
6403 assert(date.diffMonths(Date(1999, 4, 6)) == 3);
6404 assert(date.diffMonths(Date(1999, 5, 6)) == 2);
6405 assert(date.diffMonths(Date(1999, 6, 6)) == 1);
6406 assert(date.diffMonths(date) == 0);
6407 assert(date.diffMonths(Date(1999, 8, 6)) == -1);
6408 assert(date.diffMonths(Date(1999, 9, 6)) == -2);
6409 assert(date.diffMonths(Date(1999, 10, 6)) == -3);
6410 assert(date.diffMonths(Date(1999, 11, 6)) == -4);
6411 assert(date.diffMonths(Date(1999, 12, 6)) == -5);
6412 assert(date.diffMonths(Date(2000, 1, 6)) == -6);
6413 assert(date.diffMonths(Date(2000, 2, 6)) == -7);
6414 assert(date.diffMonths(Date(2000, 3, 6)) == -8);
6415 assert(date.diffMonths(Date(2000, 4, 6)) == -9);
6416 assert(date.diffMonths(Date(2000, 5, 6)) == -10);
6417 assert(date.diffMonths(Date(2000, 6, 6)) == -11);
6418 assert(date.diffMonths(Date(2000, 7, 6)) == -12);
6419 assert(date.diffMonths(Date(2000, 8, 6)) == -13);
6420
6421 assert(Date(1998, 6, 5).diffMonths(date) == -13);
6422 assert(Date(1998, 7, 5).diffMonths(date) == -12);
6423 assert(Date(1998, 8, 5).diffMonths(date) == -11);
6424 assert(Date(1998, 9, 5).diffMonths(date) == -10);
6425 assert(Date(1998, 10, 5).diffMonths(date) == -9);
6426 assert(Date(1998, 11, 5).diffMonths(date) == -8);
6427 assert(Date(1998, 12, 5).diffMonths(date) == -7);
6428 assert(Date(1999, 1, 5).diffMonths(date) == -6);
6429 assert(Date(1999, 2, 6).diffMonths(date) == -5);
6430 assert(Date(1999, 3, 6).diffMonths(date) == -4);
6431 assert(Date(1999, 4, 6).diffMonths(date) == -3);
6432 assert(Date(1999, 5, 6).diffMonths(date) == -2);
6433 assert(Date(1999, 6, 6).diffMonths(date) == -1);
6434 assert(Date(1999, 8, 6).diffMonths(date) == 1);
6435 assert(Date(1999, 9, 6).diffMonths(date) == 2);
6436 assert(Date(1999, 10, 6).diffMonths(date) == 3);
6437 assert(Date(1999, 11, 6).diffMonths(date) == 4);
6438 assert(Date(1999, 12, 6).diffMonths(date) == 5);
6439 assert(Date(2000, 1, 6).diffMonths(date) == 6);
6440 assert(Date(2000, 2, 6).diffMonths(date) == 7);
6441 assert(Date(2000, 3, 6).diffMonths(date) == 8);
6442 assert(Date(2000, 4, 6).diffMonths(date) == 9);
6443 assert(Date(2000, 5, 6).diffMonths(date) == 10);
6444 assert(Date(2000, 6, 6).diffMonths(date) == 11);
6445 assert(Date(2000, 7, 6).diffMonths(date) == 12);
6446 assert(Date(2000, 8, 6).diffMonths(date) == 13);
6447
6448 assert(date.diffMonths(Date(1999, 6, 30)) == 1);
6449 assert(date.diffMonths(Date(1999, 7, 1)) == 0);
6450 assert(date.diffMonths(Date(1999, 7, 6)) == 0);
6451 assert(date.diffMonths(Date(1999, 7, 11)) == 0);
6452 assert(date.diffMonths(Date(1999, 7, 16)) == 0);
6453 assert(date.diffMonths(Date(1999, 7, 21)) == 0);
6454 assert(date.diffMonths(Date(1999, 7, 26)) == 0);
6455 assert(date.diffMonths(Date(1999, 7, 31)) == 0);
6456 assert(date.diffMonths(Date(1999, 8, 1)) == -1);
6457
6458 assert(date.diffMonths(Date(1990, 6, 30)) == 109);
6459 assert(date.diffMonths(Date(1990, 7, 1)) == 108);
6460 assert(date.diffMonths(Date(1990, 7, 6)) == 108);
6461 assert(date.diffMonths(Date(1990, 7, 11)) == 108);
6462 assert(date.diffMonths(Date(1990, 7, 16)) == 108);
6463 assert(date.diffMonths(Date(1990, 7, 21)) == 108);
6464 assert(date.diffMonths(Date(1990, 7, 26)) == 108);
6465 assert(date.diffMonths(Date(1990, 7, 31)) == 108);
6466 assert(date.diffMonths(Date(1990, 8, 1)) == 107);
6467
6468 assert(Date(1999, 6, 30).diffMonths(date) == -1);
6469 assert(Date(1999, 7, 1).diffMonths(date) == 0);
6470 assert(Date(1999, 7, 6).diffMonths(date) == 0);
6471 assert(Date(1999, 7, 11).diffMonths(date) == 0);
6472 assert(Date(1999, 7, 16).diffMonths(date) == 0);
6473 assert(Date(1999, 7, 21).diffMonths(date) == 0);
6474 assert(Date(1999, 7, 26).diffMonths(date) == 0);
6475 assert(Date(1999, 7, 31).diffMonths(date) == 0);
6476 assert(Date(1999, 8, 1).diffMonths(date) == 1);
6477
6478 assert(Date(1990, 6, 30).diffMonths(date) == -109);
6479 assert(Date(1990, 7, 1).diffMonths(date) == -108);
6480 assert(Date(1990, 7, 6).diffMonths(date) == -108);
6481 assert(Date(1990, 7, 11).diffMonths(date) == -108);
6482 assert(Date(1990, 7, 16).diffMonths(date) == -108);
6483 assert(Date(1990, 7, 21).diffMonths(date) == -108);
6484 assert(Date(1990, 7, 26).diffMonths(date) == -108);
6485 assert(Date(1990, 7, 31).diffMonths(date) == -108);
6486 assert(Date(1990, 8, 1).diffMonths(date) == -107);
6487
6488 // Test B.C.
6489 auto dateBC = Date(-1999, 7, 6);
6490
6491 assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
6492 assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
6493 assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
6494 assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
6495 assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
6496 assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
6497 assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
6498 assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
6499 assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
6500 assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
6501 assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
6502 assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
6503 assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
6504 assert(dateBC.diffMonths(dateBC) == 0);
6505 assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
6506 assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
6507 assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
6508 assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
6509 assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
6510 assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
6511 assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
6512 assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
6513 assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
6514 assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
6515 assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
6516 assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
6517 assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
6518
6519 assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
6520 assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
6521 assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
6522 assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
6523 assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
6524 assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
6525 assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
6526 assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
6527 assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
6528 assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
6529 assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
6530 assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
6531 assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
6532 assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
6533 assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
6534 assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
6535 assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
6536 assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
6537 assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
6538 assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
6539 assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
6540 assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
6541 assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
6542 assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
6543 assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
6544 assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
6545
6546 assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
6547 assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
6548 assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
6549 assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
6550 assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
6551 assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
6552 assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
6553 assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
6554 assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
6555
6556 assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
6557 assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
6558 assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
6559 assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
6560 assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
6561 assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
6562 assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
6563 assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
6564 assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
6565
6566 assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
6567 assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
6568 assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
6569 assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
6570 assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
6571 assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
6572 assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
6573 assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
6574 assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
6575
6576 assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
6577 assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
6578 assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
6579 assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
6580 assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
6581 assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
6582 assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
6583 assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
6584 assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
6585
6586 // Test Both
6587 assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
6588 assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
6589
6590 const cdate = Date(1999, 7, 6);
6591 immutable idate = Date(1999, 7, 6);
6592 assert(date.diffMonths(date) == 0);
6593 assert(cdate.diffMonths(date) == 0);
6594 assert(idate.diffMonths(date) == 0);
6595
6596 assert(date.diffMonths(cdate) == 0);
6597 assert(cdate.diffMonths(cdate) == 0);
6598 assert(idate.diffMonths(cdate) == 0);
6599
6600 assert(date.diffMonths(idate) == 0);
6601 assert(cdate.diffMonths(idate) == 0);
6602 assert(idate.diffMonths(idate) == 0);
6603 }
6604
6605
6606 /++
6607 Whether this $(LREF Date) is in a leap year.
6608 +/
6609 @property bool isLeapYear() const @safe pure nothrow @nogc
6610 {
6611 return yearIsLeapYear(_year);
6612 }
6613
6614 @safe unittest
6615 {
6616 auto date = Date(1999, 7, 6);
6617 const cdate = Date(1999, 7, 6);
6618 immutable idate = Date(1999, 7, 6);
6619 static assert(!__traits(compiles, date.isLeapYear = true));
6620 static assert(!__traits(compiles, cdate.isLeapYear = true));
6621 static assert(!__traits(compiles, idate.isLeapYear = true));
6622 }
6623
6624
6625 /++
6626 Day of the week this $(LREF Date) is on.
6627 +/
6628 @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
6629 {
6630 return getDayOfWeek(dayOfGregorianCal);
6631 }
6632
6633 @safe unittest
6634 {
6635 const cdate = Date(1999, 7, 6);
6636 immutable idate = Date(1999, 7, 6);
6637 assert(cdate.dayOfWeek == DayOfWeek.tue);
6638 static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
6639 assert(idate.dayOfWeek == DayOfWeek.tue);
6640 static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
6641 }
6642
6643
6644 /++
6645 Day of the year this $(LREF Date) is on.
6646 +/
6647 @property ushort dayOfYear() const @safe pure nothrow @nogc
6648 {
6649 if (_month >= Month.jan && _month <= Month.dec)
6650 {
6651 immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6652 auto monthIndex = _month - Month.jan;
6653
6654 return cast(ushort)(lastDay[monthIndex] + _day);
6655 }
6656 assert(0, "Invalid month.");
6657 }
6658
6659 ///
6660 @safe unittest
6661 {
6662 assert(Date(1999, 1, 1).dayOfYear == 1);
6663 assert(Date(1999, 12, 31).dayOfYear == 365);
6664 assert(Date(2000, 12, 31).dayOfYear == 366);
6665 }
6666
6667 @safe unittest
6668 {
6669 import std.algorithm.iteration : filter;
6670 import std.range : chain;
6671
6672 foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6673 {
6674 foreach (doy; testDaysOfYear)
6675 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6676 }
6677
6678 foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6679 {
6680 foreach (doy; testDaysOfLeapYear)
6681 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6682 }
6683
6684 const cdate = Date(1999, 7, 6);
6685 immutable idate = Date(1999, 7, 6);
6686 assert(cdate.dayOfYear == 187);
6687 assert(idate.dayOfYear == 187);
6688 }
6689
6690 /++
6691 Day of the year.
6692
6693 Params:
6694 day = The day of the year to set which day of the year this
6695 $(LREF Date) is on.
6696
6697 Throws:
6698 $(REF DateTimeException,std,datetime,date) if the given day is an
6699 invalid day of the year.
6700 +/
6701 @property void dayOfYear(int day) @safe pure
6702 {
6703 setDayOfYear!true(day);
6704 }
6705
6706 private void setDayOfYear(bool useExceptions = false)(int day)
6707 {
6708 immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6709
6710 bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
6711 enum errorMsg = "Invalid day of the year.";
6712
6713 static if (useExceptions)
6714 {
6715 if (dayOutOfRange) throw new DateTimeException(errorMsg);
6716 }
6717 else
6718 {
6719 assert(!dayOutOfRange, errorMsg);
6720 }
6721
6722 foreach (i; 1 .. lastDay.length)
6723 {
6724 if (day <= lastDay[i])
6725 {
6726 _month = cast(Month)(cast(int) Month.jan + i - 1);
6727 _day = cast(ubyte)(day - lastDay[i - 1]);
6728 return;
6729 }
6730 }
6731 assert(0, "Invalid day of the year.");
6732 }
6733
6734 @safe unittest
6735 {
6736 static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
6737 {
6738 date.dayOfYear = day;
6739 assert(date.month == expected.month);
6740 assert(date.day == expected.day);
6741 }
6742
6743 foreach (doy; testDaysOfYear)
6744 {
6745 test(Date(1999, 1, 1), doy.day, doy.md);
6746 test(Date(-1, 1, 1), doy.day, doy.md);
6747 }
6748
6749 foreach (doy; testDaysOfLeapYear)
6750 {
6751 test(Date(2000, 1, 1), doy.day, doy.md);
6752 test(Date(-4, 1, 1), doy.day, doy.md);
6753 }
6754
6755 const cdate = Date(1999, 7, 6);
6756 immutable idate = Date(1999, 7, 6);
6757 static assert(!__traits(compiles, cdate.dayOfYear = 187));
6758 static assert(!__traits(compiles, idate.dayOfYear = 187));
6759 }
6760
6761
6762 /++
6763 The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6764 +/
6765 @property int dayOfGregorianCal() const @safe pure nothrow @nogc
6766 {
6767 if (isAD)
6768 {
6769 if (_year == 1)
6770 return dayOfYear;
6771
6772 int years = _year - 1;
6773 auto days = (years / 400) * daysIn400Years;
6774 years %= 400;
6775
6776 days += (years / 100) * daysIn100Years;
6777 years %= 100;
6778
6779 days += (years / 4) * daysIn4Years;
6780 years %= 4;
6781
6782 days += years * daysInYear;
6783
6784 days += dayOfYear;
6785
6786 return days;
6787 }
6788 else if (_year == 0)
6789 return dayOfYear - daysInLeapYear;
6790 else
6791 {
6792 int years = _year;
6793 auto days = (years / 400) * daysIn400Years;
6794 years %= 400;
6795
6796 days += (years / 100) * daysIn100Years;
6797 years %= 100;
6798
6799 days += (years / 4) * daysIn4Years;
6800 years %= 4;
6801
6802 if (years < 0)
6803 {
6804 days -= daysInLeapYear;
6805 ++years;
6806
6807 days += years * daysInYear;
6808
6809 days -= daysInYear - dayOfYear;
6810 }
6811 else
6812 days -= daysInLeapYear - dayOfYear;
6813
6814 return days;
6815 }
6816 }
6817
6818 ///
6819 @safe unittest
6820 {
6821 assert(Date(1, 1, 1).dayOfGregorianCal == 1);
6822 assert(Date(1, 12, 31).dayOfGregorianCal == 365);
6823 assert(Date(2, 1, 1).dayOfGregorianCal == 366);
6824
6825 assert(Date(0, 12, 31).dayOfGregorianCal == 0);
6826 assert(Date(0, 1, 1).dayOfGregorianCal == -365);
6827 assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
6828
6829 assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
6830 assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
6831 }
6832
6833 @safe unittest
6834 {
6835 import std.range : chain;
6836
6837 foreach (gd; chain(testGregDaysBC, testGregDaysAD))
6838 assert(gd.date.dayOfGregorianCal == gd.day);
6839
6840 auto date = Date(1999, 7, 6);
6841 const cdate = Date(1999, 7, 6);
6842 immutable idate = Date(1999, 7, 6);
6843 assert(date.dayOfGregorianCal == 729_941);
6844 assert(cdate.dayOfGregorianCal == 729_941);
6845 assert(idate.dayOfGregorianCal == 729_941);
6846 }
6847
6848 /++
6849 The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6850
6851 Params:
6852 day = The day of the Gregorian Calendar to set this $(LREF Date) to.
6853 +/
6854 @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
6855 {
6856 this = Date(day);
6857 }
6858
6859 ///
6860 @safe unittest
6861 {
6862 auto date = Date.init;
6863 date.dayOfGregorianCal = 1;
6864 assert(date == Date(1, 1, 1));
6865
6866 date.dayOfGregorianCal = 365;
6867 assert(date == Date(1, 12, 31));
6868
6869 date.dayOfGregorianCal = 366;
6870 assert(date == Date(2, 1, 1));
6871
6872 date.dayOfGregorianCal = 0;
6873 assert(date == Date(0, 12, 31));
6874
6875 date.dayOfGregorianCal = -365;
6876 assert(date == Date(-0, 1, 1));
6877
6878 date.dayOfGregorianCal = -366;
6879 assert(date == Date(-1, 12, 31));
6880
6881 date.dayOfGregorianCal = 730_120;
6882 assert(date == Date(2000, 1, 1));
6883
6884 date.dayOfGregorianCal = 734_137;
6885 assert(date == Date(2010, 12, 31));
6886 }
6887
6888 @safe unittest
6889 {
6890 auto date = Date(1999, 7, 6);
6891 const cdate = Date(1999, 7, 6);
6892 immutable idate = Date(1999, 7, 6);
6893 date.dayOfGregorianCal = 187;
6894 assert(date.dayOfGregorianCal == 187);
6895 static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
6896 static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
6897 }
6898
6899
6900 /++
6901 The ISO 8601 week and year of the year that this $(LREF Date) is in.
6902
6903 Returns:
6904 An anonymous struct with the members $(D isoWeekYear) for the
6905 resulting year and $(D isoWeek) for the resulting ISO week.
6906
6907 See_Also:
6908 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6909 +/
6910 @property auto isoWeekAndYear() const @safe pure nothrow
6911 {
6912 struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
6913
6914 immutable weekday = dayOfWeek;
6915 immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
6916 immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
6917
6918 try
6919 {
6920 if (week == 53)
6921 {
6922 switch (Date(_year + 1, 1, 1).dayOfWeek)
6923 {
6924 case DayOfWeek.mon:
6925 case DayOfWeek.tue:
6926 case DayOfWeek.wed:
6927 case DayOfWeek.thu:
6928 return ISOWeekAndYear(cast(short) (_year + 1), 1);
6929 case DayOfWeek.fri:
6930 case DayOfWeek.sat:
6931 case DayOfWeek.sun:
6932 return ISOWeekAndYear(_year, 53);
6933 default:
6934 assert(0, "Invalid ISO Week");
6935 }
6936 }
6937 else if (week > 0)
6938 return ISOWeekAndYear(_year, cast(ubyte) week);
6939 else
6940 return Date(_year - 1, 12, 31).isoWeekAndYear;
6941 }
6942 catch (Exception e)
6943 assert(0, "Date's constructor threw.");
6944 }
6945
6946 /++
6947 The ISO 8601 week of the year that this $(LREF Date) is in.
6948
6949 See_Also:
6950 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6951 +/
6952 @property ubyte isoWeek() const @safe pure nothrow
6953 {
6954 return isoWeekAndYear().isoWeek;
6955 }
6956
6957 @safe unittest
6958 {
6959 // Test A.D.
6960 assert(Date(2009, 12, 28).isoWeek == 53);
6961 assert(Date(2009, 12, 29).isoWeek == 53);
6962 assert(Date(2009, 12, 30).isoWeek == 53);
6963 assert(Date(2009, 12, 31).isoWeek == 53);
6964 assert(Date(2010, 1, 1).isoWeek == 53);
6965 assert(Date(2010, 1, 2).isoWeek == 53);
6966 assert(Date(2010, 1, 3).isoWeek == 53);
6967 assert(Date(2010, 1, 4).isoWeek == 1);
6968 assert(Date(2010, 1, 5).isoWeek == 1);
6969 assert(Date(2010, 1, 6).isoWeek == 1);
6970 assert(Date(2010, 1, 7).isoWeek == 1);
6971 assert(Date(2010, 1, 8).isoWeek == 1);
6972 assert(Date(2010, 1, 9).isoWeek == 1);
6973 assert(Date(2010, 1, 10).isoWeek == 1);
6974 assert(Date(2010, 1, 11).isoWeek == 2);
6975 assert(Date(2010, 12, 31).isoWeek == 52);
6976
6977 assert(Date(2004, 12, 26).isoWeek == 52);
6978 assert(Date(2004, 12, 27).isoWeek == 53);
6979 assert(Date(2004, 12, 28).isoWeek == 53);
6980 assert(Date(2004, 12, 29).isoWeek == 53);
6981 assert(Date(2004, 12, 30).isoWeek == 53);
6982 assert(Date(2004, 12, 31).isoWeek == 53);
6983 assert(Date(2005, 1, 1).isoWeek == 53);
6984 assert(Date(2005, 1, 2).isoWeek == 53);
6985
6986 assert(Date(2005, 12, 31).isoWeek == 52);
6987 assert(Date(2007, 1, 1).isoWeek == 1);
6988
6989 assert(Date(2007, 12, 30).isoWeek == 52);
6990 assert(Date(2007, 12, 31).isoWeek == 1);
6991 assert(Date(2008, 1, 1).isoWeek == 1);
6992
6993 assert(Date(2008, 12, 28).isoWeek == 52);
6994 assert(Date(2008, 12, 29).isoWeek == 1);
6995 assert(Date(2008, 12, 30).isoWeek == 1);
6996 assert(Date(2008, 12, 31).isoWeek == 1);
6997 assert(Date(2009, 1, 1).isoWeek == 1);
6998 assert(Date(2009, 1, 2).isoWeek == 1);
6999 assert(Date(2009, 1, 3).isoWeek == 1);
7000 assert(Date(2009, 1, 4).isoWeek == 1);
7001
7002 // Test B.C.
7003 // The algorithm should work identically for both A.D. and B.C. since
7004 // it doesn't really take the year into account, so B.C. testing
7005 // probably isn't really needed.
7006 assert(Date(0, 12, 31).isoWeek == 52);
7007 assert(Date(0, 1, 4).isoWeek == 1);
7008 assert(Date(0, 1, 1).isoWeek == 52);
7009
7010 const cdate = Date(1999, 7, 6);
7011 immutable idate = Date(1999, 7, 6);
7012 assert(cdate.isoWeek == 27);
7013 static assert(!__traits(compiles, cdate.isoWeek = 3));
7014 assert(idate.isoWeek == 27);
7015 static assert(!__traits(compiles, idate.isoWeek = 3));
7016 }
7017
7018 /++
7019 The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
7020
7021 May differ from $(LREF year) between 28 December and 4 January.
7022
7023 See_Also:
7024 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
7025 +/
7026 @property short isoWeekYear() const @safe pure nothrow
7027 {
7028 return isoWeekAndYear().isoWeekYear;
7029 }
7030
7031 @safe unittest
7032 {
7033 // Test A.D.
7034 assert(Date(2009, 12, 28).isoWeekYear == 2009);
7035 assert(Date(2009, 12, 29).isoWeekYear == 2009);
7036 assert(Date(2009, 12, 30).isoWeekYear == 2009);
7037 assert(Date(2009, 12, 31).isoWeekYear == 2009);
7038 assert(Date(2010, 1, 1).isoWeekYear == 2009);
7039 assert(Date(2010, 1, 2).isoWeekYear == 2009);
7040 assert(Date(2010, 1, 3).isoWeekYear == 2009);
7041 assert(Date(2010, 1, 4).isoWeekYear == 2010);
7042 assert(Date(2010, 1, 5).isoWeekYear == 2010);
7043 assert(Date(2010, 1, 6).isoWeekYear == 2010);
7044 assert(Date(2010, 1, 7).isoWeekYear == 2010);
7045 assert(Date(2010, 1, 8).isoWeekYear == 2010);
7046 assert(Date(2010, 1, 9).isoWeekYear == 2010);
7047 assert(Date(2010, 1, 10).isoWeekYear == 2010);
7048 assert(Date(2010, 1, 11).isoWeekYear == 2010);
7049 assert(Date(2010, 12, 31).isoWeekYear == 2010);
7050
7051 assert(Date(2004, 12, 26).isoWeekYear == 2004);
7052 assert(Date(2004, 12, 27).isoWeekYear == 2004);
7053 assert(Date(2004, 12, 28).isoWeekYear == 2004);
7054 assert(Date(2004, 12, 29).isoWeekYear == 2004);
7055 assert(Date(2004, 12, 30).isoWeekYear == 2004);
7056 assert(Date(2004, 12, 31).isoWeekYear == 2004);
7057 assert(Date(2005, 1, 1).isoWeekYear == 2004);
7058 assert(Date(2005, 1, 2).isoWeekYear == 2004);
7059 assert(Date(2005, 1, 3).isoWeekYear == 2005);
7060
7061 assert(Date(2005, 12, 31).isoWeekYear == 2005);
7062 assert(Date(2007, 1, 1).isoWeekYear == 2007);
7063
7064 assert(Date(2007, 12, 30).isoWeekYear == 2007);
7065 assert(Date(2007, 12, 31).isoWeekYear == 2008);
7066 assert(Date(2008, 1, 1).isoWeekYear == 2008);
7067
7068 assert(Date(2008, 12, 28).isoWeekYear == 2008);
7069 assert(Date(2008, 12, 29).isoWeekYear == 2009);
7070 assert(Date(2008, 12, 30).isoWeekYear == 2009);
7071 assert(Date(2008, 12, 31).isoWeekYear == 2009);
7072 assert(Date(2009, 1, 1).isoWeekYear == 2009);
7073 assert(Date(2009, 1, 2).isoWeekYear == 2009);
7074 assert(Date(2009, 1, 3).isoWeekYear == 2009);
7075 assert(Date(2009, 1, 4).isoWeekYear == 2009);
7076
7077 // Test B.C.
7078 assert(Date(0, 12, 31).isoWeekYear == 0);
7079 assert(Date(0, 1, 4).isoWeekYear == 0);
7080 assert(Date(0, 1, 1).isoWeekYear == -1);
7081
7082 const cdate = Date(1999, 7, 6);
7083 immutable idate = Date(1999, 7, 6);
7084 assert(cdate.isoWeekYear == 1999);
7085 assert(idate.isoWeekYear == 1999);
7086 }
7087
7088 static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
7089 {
7090 immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
7091 immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
7092
7093 Date date;
7094 date._year = isoWeekYear;
7095 date._month = Month.jan;
7096 date._day = 3;
7097 immutable startOfYear = date.dayOfWeek;
7098 return date._addDays(dayOffset - startOfYear);
7099 }
7100
7101 @safe unittest
7102 {
7103 // Test -30000 days to 30000 days for matching construction <-> deconstruction
7104 Date date = Date(1, 1, 1);
7105 date._addDays(-30_000);
7106 foreach (day; 0 .. 60_000)
7107 {
7108 const year = date.isoWeekYear;
7109 const dow = date.dayOfWeek;
7110 const isoWeek = date.isoWeek;
7111 const reversed = Date.fromISOWeek(year, isoWeek, dow);
7112 assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
7113 date = date._addDays(1);
7114 }
7115 }
7116
7117
7118 /++
7119 $(LREF Date) for the last day in the month that this $(LREF Date) is in.
7120 +/
7121 @property Date endOfMonth() const @safe pure nothrow
7122 {
7123 try
7124 return Date(_year, _month, maxDay(_year, _month));
7125 catch (Exception e)
7126 assert(0, "Date's constructor threw.");
7127 }
7128
7129 ///
7130 @safe unittest
7131 {
7132 assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
7133 assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
7134 assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
7135 assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
7136 }
7137
7138 @safe unittest
7139 {
7140 // Test A.D.
7141 assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
7142 assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
7143 assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
7144 assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
7145 assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
7146 assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
7147 assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
7148 assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
7149 assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
7150 assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
7151 assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
7152 assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
7153 assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
7154
7155 // Test B.C.
7156 assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
7157 assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
7158 assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
7159 assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
7160 assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
7161 assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
7162 assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
7163 assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
7164 assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
7165 assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
7166 assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
7167 assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
7168 assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
7169
7170 const cdate = Date(1999, 7, 6);
7171 immutable idate = Date(1999, 7, 6);
7172 static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
7173 static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
7174 }
7175
7176
7177 /++
7178 The last day in the month that this $(LREF Date) is in.
7179 +/
7180 @property ubyte daysInMonth() const @safe pure nothrow @nogc
7181 {
7182 return maxDay(_year, _month);
7183 }
7184
7185 ///
7186 @safe unittest
7187 {
7188 assert(Date(1999, 1, 6).daysInMonth == 31);
7189 assert(Date(1999, 2, 7).daysInMonth == 28);
7190 assert(Date(2000, 2, 7).daysInMonth == 29);
7191 assert(Date(2000, 6, 4).daysInMonth == 30);
7192 }
7193
7194 @safe unittest
7195 {
7196 // Test A.D.
7197 assert(Date(1999, 1, 1).daysInMonth == 31);
7198 assert(Date(1999, 2, 1).daysInMonth == 28);
7199 assert(Date(2000, 2, 1).daysInMonth == 29);
7200 assert(Date(1999, 3, 1).daysInMonth == 31);
7201 assert(Date(1999, 4, 1).daysInMonth == 30);
7202 assert(Date(1999, 5, 1).daysInMonth == 31);
7203 assert(Date(1999, 6, 1).daysInMonth == 30);
7204 assert(Date(1999, 7, 1).daysInMonth == 31);
7205 assert(Date(1999, 8, 1).daysInMonth == 31);
7206 assert(Date(1999, 9, 1).daysInMonth == 30);
7207 assert(Date(1999, 10, 1).daysInMonth == 31);
7208 assert(Date(1999, 11, 1).daysInMonth == 30);
7209 assert(Date(1999, 12, 1).daysInMonth == 31);
7210
7211 // Test B.C.
7212 assert(Date(-1999, 1, 1).daysInMonth == 31);
7213 assert(Date(-1999, 2, 1).daysInMonth == 28);
7214 assert(Date(-2000, 2, 1).daysInMonth == 29);
7215 assert(Date(-1999, 3, 1).daysInMonth == 31);
7216 assert(Date(-1999, 4, 1).daysInMonth == 30);
7217 assert(Date(-1999, 5, 1).daysInMonth == 31);
7218 assert(Date(-1999, 6, 1).daysInMonth == 30);
7219 assert(Date(-1999, 7, 1).daysInMonth == 31);
7220 assert(Date(-1999, 8, 1).daysInMonth == 31);
7221 assert(Date(-1999, 9, 1).daysInMonth == 30);
7222 assert(Date(-1999, 10, 1).daysInMonth == 31);
7223 assert(Date(-1999, 11, 1).daysInMonth == 30);
7224 assert(Date(-1999, 12, 1).daysInMonth == 31);
7225
7226 const cdate = Date(1999, 7, 6);
7227 immutable idate = Date(1999, 7, 6);
7228 static assert(!__traits(compiles, cdate.daysInMonth = 30));
7229 static assert(!__traits(compiles, idate.daysInMonth = 30));
7230 }
7231
7232
7233 /++
7234 Whether the current year is a date in A.D.
7235 +/
7236 @property bool isAD() const @safe pure nothrow @nogc
7237 {
7238 return _year > 0;
7239 }
7240
7241 ///
7242 @safe unittest
7243 {
7244 assert(Date(1, 1, 1).isAD);
7245 assert(Date(2010, 12, 31).isAD);
7246 assert(!Date(0, 12, 31).isAD);
7247 assert(!Date(-2010, 1, 1).isAD);
7248 }
7249
7250 @safe unittest
7251 {
7252 assert(Date(2010, 7, 4).isAD);
7253 assert(Date(1, 1, 1).isAD);
7254 assert(!Date(0, 1, 1).isAD);
7255 assert(!Date(-1, 1, 1).isAD);
7256 assert(!Date(-2010, 7, 4).isAD);
7257
7258 const cdate = Date(1999, 7, 6);
7259 immutable idate = Date(1999, 7, 6);
7260 assert(cdate.isAD);
7261 assert(idate.isAD);
7262 }
7263
7264
7265 /++
7266 The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
7267 $(LREF Date) at noon (since the Julian day changes at noon).
7268 +/
7269 @property long julianDay() const @safe pure nothrow @nogc
7270 {
7271 return dayOfGregorianCal + 1_721_425;
7272 }
7273
7274 @safe unittest
7275 {
7276 assert(Date(-4713, 11, 24).julianDay == 0);
7277 assert(Date(0, 12, 31).julianDay == 1_721_425);
7278 assert(Date(1, 1, 1).julianDay == 1_721_426);
7279 assert(Date(1582, 10, 15).julianDay == 2_299_161);
7280 assert(Date(1858, 11, 17).julianDay == 2_400_001);
7281 assert(Date(1982, 1, 4).julianDay == 2_444_974);
7282 assert(Date(1996, 3, 31).julianDay == 2_450_174);
7283 assert(Date(2010, 8, 24).julianDay == 2_455_433);
7284
7285 const cdate = Date(1999, 7, 6);
7286 immutable idate = Date(1999, 7, 6);
7287 assert(cdate.julianDay == 2_451_366);
7288 assert(idate.julianDay == 2_451_366);
7289 }
7290
7291
7292 /++
7293 The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
7294 any time on this date (since, the modified Julian day changes at
7295 midnight).
7296 +/
7297 @property long modJulianDay() const @safe pure nothrow @nogc
7298 {
7299 return julianDay - 2_400_001;
7300 }
7301
7302 @safe unittest
7303 {
7304 assert(Date(1858, 11, 17).modJulianDay == 0);
7305 assert(Date(2010, 8, 24).modJulianDay == 55_432);
7306
7307 const cdate = Date(1999, 7, 6);
7308 immutable idate = Date(1999, 7, 6);
7309 assert(cdate.modJulianDay == 51_365);
7310 assert(idate.modJulianDay == 51_365);
7311 }
7312
7313
7314 /++
7315 Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
7316 If `writer` is set, the resulting string will be written directly
7317 to it.
7318
7319 Params:
7320 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7321 Returns:
7322 A `string` when not using an output range; `void` otherwise.
7323 +/
7324 string toISOString() const @safe pure nothrow
7325 {
7326 import std.array : appender;
7327 auto w = appender!string();
7328 w.reserve(8);
7329 try
7330 toISOString(w);
7331 catch (Exception e)
7332 assert(0, "toISOString() threw.");
7333 return w.data;
7334 }
7335
7336 ///
7337 @safe unittest
7338 {
7339 assert(Date(2010, 7, 4).toISOString() == "20100704");
7340 assert(Date(1998, 12, 25).toISOString() == "19981225");
7341 assert(Date(0, 1, 5).toISOString() == "00000105");
7342 assert(Date(-4, 1, 5).toISOString() == "-00040105");
7343 }
7344
7345 @safe unittest
7346 {
7347 // Test A.D.
7348 assert(Date(9, 12, 4).toISOString() == "00091204");
7349 assert(Date(99, 12, 4).toISOString() == "00991204");
7350 assert(Date(999, 12, 4).toISOString() == "09991204");
7351 assert(Date(9999, 7, 4).toISOString() == "99990704");
7352 assert(Date(10000, 10, 20).toISOString() == "+100001020");
7353
7354 // Test B.C.
7355 assert(Date(0, 12, 4).toISOString() == "00001204");
7356 assert(Date(-9, 12, 4).toISOString() == "-00091204");
7357 assert(Date(-99, 12, 4).toISOString() == "-00991204");
7358 assert(Date(-999, 12, 4).toISOString() == "-09991204");
7359 assert(Date(-9999, 7, 4).toISOString() == "-99990704");
7360 assert(Date(-10000, 10, 20).toISOString() == "-100001020");
7361
7362 const cdate = Date(1999, 7, 6);
7363 immutable idate = Date(1999, 7, 6);
7364 assert(cdate.toISOString() == "19990706");
7365 assert(idate.toISOString() == "19990706");
7366 }
7367
7368 /// ditto
7369 void toISOString(W)(ref W writer) const
7370 if (isOutputRange!(W, char))
7371 {
7372 import std.format.write : formattedWrite;
7373 if (_year >= 0)
7374 {
7375 if (_year < 10_000)
7376 formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
7377 else
7378 formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
7379 }
7380 else if (_year > -10_000)
7381 formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
7382 else
7383 formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
7384 }
7385
7386 @safe pure unittest
7387 {
7388 import std.array : appender;
7389
7390 auto w = appender!(char[])();
7391 Date(2010, 7, 4).toISOString(w);
7392 assert(w.data == "20100704");
7393 w.clear();
7394 Date(1998, 12, 25).toISOString(w);
7395 assert(w.data == "19981225");
7396 }
7397
7398 /++
7399 Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
7400 If `writer` is set, the resulting string will be written directly
7401 to it.
7402
7403 Params:
7404 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7405 Returns:
7406 A `string` when not using an output range; `void` otherwise.
7407 +/
7408 string toISOExtString() const @safe pure nothrow
7409 {
7410 import std.array : appender;
7411 auto w = appender!string();
7412 w.reserve(10);
7413 try
7414 toISOExtString(w);
7415 catch (Exception e)
7416 assert(0, "toISOExtString() threw.");
7417 return w.data;
7418 }
7419
7420 ///
7421 @safe unittest
7422 {
7423 assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
7424 assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
7425 assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
7426 assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
7427 }
7428
7429 @safe unittest
7430 {
7431 // Test A.D.
7432 assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
7433 assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
7434 assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
7435 assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
7436 assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
7437
7438 // Test B.C.
7439 assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
7440 assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
7441 assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
7442 assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
7443 assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
7444 assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
7445
7446 const cdate = Date(1999, 7, 6);
7447 immutable idate = Date(1999, 7, 6);
7448 assert(cdate.toISOExtString() == "1999-07-06");
7449 assert(idate.toISOExtString() == "1999-07-06");
7450 }
7451
7452 /// ditto
7453 void toISOExtString(W)(ref W writer) const
7454 if (isOutputRange!(W, char))
7455 {
7456 import std.format.write : formattedWrite;
7457 if (_year >= 0)
7458 {
7459 if (_year < 10_000)
7460 formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
7461 else
7462 formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
7463 }
7464 else if (_year > -10_000)
7465 formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
7466 else
7467 formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
7468 }
7469
7470 @safe pure unittest
7471 {
7472 import std.array : appender;
7473
7474 auto w = appender!(char[])();
7475 Date(2010, 7, 4).toISOExtString(w);
7476 assert(w.data == "2010-07-04");
7477 w.clear();
7478 Date(-4, 1, 5).toISOExtString(w);
7479 assert(w.data == "-0004-01-05");
7480 }
7481
7482 /++
7483 Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
7484 If `writer` is set, the resulting string will be written directly
7485 to it.
7486
7487 Params:
7488 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7489 Returns:
7490 A `string` when not using an output range; `void` otherwise.
7491 +/
7492 string toSimpleString() const @safe pure nothrow
7493 {
7494 import std.array : appender;
7495 auto w = appender!string();
7496 w.reserve(11);
7497 try
7498 toSimpleString(w);
7499 catch (Exception e)
7500 assert(0, "toSimpleString() threw.");
7501 return w.data;
7502 }
7503
7504 ///
7505 @safe unittest
7506 {
7507 assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
7508 assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
7509 assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
7510 assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
7511 }
7512
7513 @safe unittest
7514 {
7515 // Test A.D.
7516 assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
7517 assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
7518 assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
7519 assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
7520 assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
7521
7522 // Test B.C.
7523 assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
7524 assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
7525 assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
7526 assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
7527 assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
7528 assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
7529
7530 const cdate = Date(1999, 7, 6);
7531 immutable idate = Date(1999, 7, 6);
7532 assert(cdate.toSimpleString() == "1999-Jul-06");
7533 assert(idate.toSimpleString() == "1999-Jul-06");
7534 }
7535
7536 /// ditto
7537 void toSimpleString(W)(ref W writer) const
7538 if (isOutputRange!(W, char))
7539 {
7540 import std.format.write : formattedWrite;
7541 if (_year >= 0)
7542 {
7543 if (_year < 10_000)
7544 formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
7545 else
7546 formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
7547 }
7548 else if (_year > -10_000)
7549 formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
7550 else
7551 formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
7552 }
7553
7554 @safe pure unittest
7555 {
7556 import std.array : appender;
7557
7558 auto w = appender!(char[])();
7559 Date(9, 12, 4).toSimpleString(w);
7560 assert(w.data == "0009-Dec-04");
7561 w.clear();
7562 Date(-10000, 10, 20).toSimpleString(w);
7563 assert(w.data == "-10000-Oct-20");
7564 }
7565
7566 /++
7567 Converts this $(LREF Date) to a string.
7568
7569 This function exists to make it easy to convert a $(LREF Date) to a
7570 string for code that does not care what the exact format is - just that
7571 it presents the information in a clear manner. It also makes it easy to
7572 simply convert a $(LREF Date) to a string when using functions such as
7573 `to!string`, `format`, or `writeln` which use toString to convert
7574 user-defined types. So, it is unlikely that much code will call
7575 toString directly.
7576
7577 The format of the string is purposefully unspecified, and code that
7578 cares about the format of the string should use `toISOString`,
7579 `toISOExtString`, `toSimpleString`, or some other custom formatting
7580 function that explicitly generates the format that the code needs. The
7581 reason is that the code is then clear about what format it's using,
7582 making it less error-prone to maintain the code and interact with other
7583 software that consumes the generated strings. It's for this same reason
7584 $(LREF Date) has no `fromString` function, whereas it does have
7585 `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
7586
7587 The format returned by toString may or may not change in the future.
7588 +/
7589 string toString() const @safe pure nothrow
7590 {
7591 return toSimpleString();
7592 }
7593
7594 @safe unittest
7595 {
7596 auto date = Date(1999, 7, 6);
7597 const cdate = Date(1999, 7, 6);
7598 immutable idate = Date(1999, 7, 6);
7599 assert(date.toString());
7600 assert(cdate.toString());
7601 assert(idate.toString());
7602 }
7603
7604 /// ditto
7605 void toString(W)(ref W writer) const
7606 if (isOutputRange!(W, char))
7607 {
7608 toSimpleString(writer);
7609 }
7610
7611 /++
7612 Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
7613 is stripped from the given string.
7614
7615 Params:
7616 isoString = A string formatted in the ISO format for dates.
7617
7618 Throws:
7619 $(REF DateTimeException,std,datetime,date) if the given string is
7620 not in the ISO format or if the resulting $(LREF Date) would not be
7621 valid.
7622 +/
7623 static Date fromISOString(S)(scope const S isoString) @safe pure
7624 if (isSomeString!S)
7625 {
7626 import std.algorithm.searching : startsWith;
7627 import std.conv : to, text, ConvException;
7628 import std.exception : enforce;
7629 import std.string : strip;
7630
7631 auto str = isoString.strip;
7632
7633 enforce!DateTimeException(str.length >= 8, text("Invalid format for Date.fromISOString: ", isoString));
7634
7635 int day, month, year;
7636 auto yearStr = str[0 .. $ - 4];
7637
7638 try
7639 {
7640 // using conversion to uint plus cast because it checks for +/-
7641 // for us quickly while throwing ConvException
7642 day = cast(int) to!uint(str[$ - 2 .. $]);
7643 month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
7644
7645 if (yearStr.length > 4)
7646 {
7647 enforce!DateTimeException(yearStr.startsWith('-', '+'),
7648 text("Invalid format for Date.fromISOString: ", isoString));
7649 year = to!int(yearStr);
7650 }
7651 else
7652 {
7653 year = cast(int) to!uint(yearStr);
7654 }
7655 }
7656 catch (ConvException)
7657 {
7658 throw new DateTimeException(text("Invalid format for Date.fromISOString: ", isoString));
7659 }
7660
7661 return Date(year, month, day);
7662 }
7663
7664 ///
7665 @safe unittest
7666 {
7667 assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
7668 assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
7669 assert(Date.fromISOString("00000105") == Date(0, 1, 5));
7670 assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
7671 assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
7672 }
7673
7674 @safe unittest
7675 {
7676 assertThrown!DateTimeException(Date.fromISOString(""));
7677 assertThrown!DateTimeException(Date.fromISOString("990704"));
7678 assertThrown!DateTimeException(Date.fromISOString("0100704"));
7679 assertThrown!DateTimeException(Date.fromISOString("2010070"));
7680 assertThrown!DateTimeException(Date.fromISOString("2010070 "));
7681 assertThrown!DateTimeException(Date.fromISOString("120100704"));
7682 assertThrown!DateTimeException(Date.fromISOString("-0100704"));
7683 assertThrown!DateTimeException(Date.fromISOString("+0100704"));
7684 assertThrown!DateTimeException(Date.fromISOString("2010070a"));
7685 assertThrown!DateTimeException(Date.fromISOString("20100a04"));
7686 assertThrown!DateTimeException(Date.fromISOString("2010a704"));
7687
7688 assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
7689 assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
7690 assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
7691 assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
7692 assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
7693 assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
7694 assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
7695 assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
7696 assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
7697 assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
7698 assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
7699 assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
7700 assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
7701 assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
7702 assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
7703 assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
7704 assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
7705
7706 assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
7707 assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
7708 assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
7709 assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
7710 assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
7711 assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
7712 assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
7713 assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
7714 assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
7715 assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
7716
7717 assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
7718 assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
7719 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
7720 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
7721 assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
7722 assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
7723 assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
7724 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
7725 assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
7726 assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
7727 assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
7728
7729 assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
7730 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
7731
7732 assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
7733 assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
7734 assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
7735 assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
7736 assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
7737 assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
7738 }
7739
7740 // https://issues.dlang.org/show_bug.cgi?id=17801
7741 @safe unittest
7742 {
7743 import std.conv : to;
7744 import std.meta : AliasSeq;
7745 static foreach (C; AliasSeq!(char, wchar, dchar))
7746 {
7747 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7748 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
7749 }
7750 }
7751
7752
7753 /++
7754 Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
7755 Whitespace is stripped from the given string.
7756
7757 Params:
7758 isoExtString = A string formatted in the ISO Extended format for
7759 dates.
7760
7761 Throws:
7762 $(REF DateTimeException,std,datetime,date) if the given string is
7763 not in the ISO Extended format or if the resulting $(LREF Date)
7764 would not be valid.
7765 +/
7766 static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
7767 if (isSomeString!(S))
7768 {
7769 import std.algorithm.searching : startsWith;
7770 import std.conv : to, ConvException;
7771 import std.format : format;
7772 import std.string : strip;
7773
7774 auto str = strip(isoExtString);
7775 short year;
7776 ubyte month, day;
7777
7778 if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
7779 throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7780
7781 auto yearStr = str[0 .. $-6];
7782 auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7783 if ((yearStr.length > 4) != signAtBegining)
7784 {
7785 throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7786 }
7787
7788 try
7789 {
7790 day = to!ubyte(str[$-2 .. $]);
7791 month = to!ubyte(str[$-5 .. $-3]);
7792 year = to!short(yearStr);
7793 }
7794 catch (ConvException)
7795 {
7796 throw new DateTimeException(format("Invalid format for Date.fromISOExtString: %s", isoExtString));
7797 }
7798
7799 return Date(year, month, day);
7800 }
7801
7802 ///
7803 @safe unittest
7804 {
7805 assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
7806 assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
7807 assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
7808 assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
7809 assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
7810 }
7811
7812 @safe unittest
7813 {
7814 assertThrown!DateTimeException(Date.fromISOExtString(""));
7815 assertThrown!DateTimeException(Date.fromISOExtString("990704"));
7816 assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
7817 assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
7818 assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
7819 assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
7820 assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
7821 assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
7822 assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
7823 assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
7824 assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
7825
7826 assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
7827 assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
7828 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
7829 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
7830 assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
7831 assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
7832 assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
7833 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
7834 assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
7835 assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
7836 assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
7837 assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
7838 assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
7839 assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
7840 assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
7841 assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
7842 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
7843
7844 assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
7845 assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
7846 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
7847 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
7848 assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
7849 assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
7850 assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
7851 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
7852 assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
7853 assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
7854
7855 assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
7856 assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
7857 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
7858 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
7859 assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
7860 assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
7861 assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
7862 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
7863 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
7864 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
7865 assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
7866
7867 assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
7868 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
7869
7870 assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
7871 assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
7872 assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
7873 assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
7874 assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
7875 assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
7876 }
7877
7878 // https://issues.dlang.org/show_bug.cgi?id=17801
7879 @safe unittest
7880 {
7881 import std.conv : to;
7882 import std.meta : AliasSeq;
7883 static foreach (C; AliasSeq!(char, wchar, dchar))
7884 {
7885 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
7886 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
7887 }
7888 }
7889
7890
7891 /++
7892 Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
7893 Whitespace is stripped from the given string.
7894
7895 Params:
7896 simpleString = A string formatted in the way that toSimpleString
7897 formats dates.
7898
7899 Throws:
7900 $(REF DateTimeException,std,datetime,date) if the given string is
7901 not in the correct format or if the resulting $(LREF Date) would not
7902 be valid.
7903 +/
7904 static Date fromSimpleString(S)(scope const S simpleString) @safe pure
7905 if (isSomeString!(S))
7906 {
7907 import std.algorithm.searching : startsWith;
7908 import std.conv : to, ConvException;
7909 import std.format : format;
7910 import std.string : strip;
7911
7912 auto str = strip(simpleString);
7913
7914 if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
7915 throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7916
7917 int year;
7918 uint day;
7919 auto month = monthFromString(str[$ - 6 .. $ - 3]);
7920 auto yearStr = str[0 .. $ - 7];
7921 auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7922 if ((yearStr.length > 4) != signAtBegining)
7923 {
7924 throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7925 }
7926
7927 try
7928 {
7929 day = to!uint(str[$ - 2 .. $]);
7930 year = to!int(yearStr);
7931 }
7932 catch (ConvException)
7933 {
7934 throw new DateTimeException(format!"Invalid format for Date.fromSimpleString: %s"(simpleString));
7935 }
7936
7937 return Date(year, month, day);
7938 }
7939
7940 ///
7941 @safe unittest
7942 {
7943 assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
7944 assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
7945 assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
7946 assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
7947 assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
7948 }
7949
7950 @safe unittest
7951 {
7952 assertThrown!DateTimeException(Date.fromSimpleString(""));
7953 assertThrown!DateTimeException(Date.fromSimpleString("990704"));
7954 assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
7955 assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
7956 assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
7957 assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
7958 assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
7959 assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
7960 assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
7961 assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
7962 assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
7963
7964 assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
7965 assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
7966 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
7967 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
7968 assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
7969 assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
7970 assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
7971 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
7972 assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
7973 assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
7974 assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
7975 assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
7976 assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
7977 assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
7978 assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
7979 assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
7980 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
7981
7982 assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
7983 assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
7984 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
7985 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
7986 assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
7987 assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
7988 assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
7989 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
7990 assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
7991 assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
7992
7993 assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
7994 assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
7995 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
7996 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
7997 assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
7998 assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
7999 assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
8000 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
8001 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
8002 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
8003 assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
8004
8005 assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
8006 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
8007
8008 assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
8009 assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
8010 assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
8011 assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
8012 assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
8013 assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
8014 }
8015
8016 // https://issues.dlang.org/show_bug.cgi?id=17801
8017 @safe unittest
8018 {
8019 import std.conv : to;
8020 import std.meta : AliasSeq;
8021 static foreach (C; AliasSeq!(char, wchar, dchar))
8022 {
8023 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
8024 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
8025 }
8026 }
8027
8028
8029 /++
8030 Returns the $(LREF Date) farthest in the past which is representable by
8031 $(LREF Date).
8032 +/
8033 @property static Date min() @safe pure nothrow @nogc
8034 {
8035 auto date = Date.init;
8036 date._year = short.min;
8037 date._month = Month.jan;
8038 date._day = 1;
8039
8040 return date;
8041 }
8042
8043 @safe unittest
8044 {
8045 assert(Date.min.year < 0);
8046 assert(Date.min < Date.max);
8047 }
8048
8049
8050 /++
8051 Returns the $(LREF Date) farthest in the future which is representable
8052 by $(LREF Date).
8053 +/
8054 @property static Date max() @safe pure nothrow @nogc
8055 {
8056 auto date = Date.init;
8057 date._year = short.max;
8058 date._month = Month.dec;
8059 date._day = 31;
8060
8061 return date;
8062 }
8063
8064 @safe unittest
8065 {
8066 assert(Date.max.year > 0);
8067 assert(Date.max > Date.min);
8068 }
8069
8070
8071 private:
8072
8073 /+
8074 Whether the given values form a valid date.
8075
8076 Params:
8077 year = The year to test.
8078 month = The month of the Gregorian Calendar to test.
8079 day = The day of the month to test.
8080 +/
8081 static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
8082 {
8083 if (!valid!"months"(month))
8084 return false;
8085 return valid!"days"(year, month, day);
8086 }
8087
8088
8089 package:
8090
8091 /+
8092 Adds the given number of days to this $(LREF Date). A negative number
8093 will subtract.
8094
8095 The month will be adjusted along with the day if the number of days
8096 added (or subtracted) would overflow (or underflow) the current month.
8097 The year will be adjusted along with the month if the increase (or
8098 decrease) to the month would cause it to overflow (or underflow) the
8099 current year.
8100
8101 `_addDays(numDays)` is effectively equivalent to
8102 $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
8103
8104 Params:
8105 days = The number of days to add to this Date.
8106 +/
8107 ref Date _addDays(long days) return @safe pure nothrow @nogc
8108 {
8109 dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
8110 return this;
8111 }
8112
8113 @safe unittest
8114 {
8115 // Test A.D.
8116 {
8117 auto date = Date(1999, 2, 28);
8118 date._addDays(1);
8119 assert(date == Date(1999, 3, 1));
8120 date._addDays(-1);
8121 assert(date == Date(1999, 2, 28));
8122 }
8123
8124 {
8125 auto date = Date(2000, 2, 28);
8126 date._addDays(1);
8127 assert(date == Date(2000, 2, 29));
8128 date._addDays(1);
8129 assert(date == Date(2000, 3, 1));
8130 date._addDays(-1);
8131 assert(date == Date(2000, 2, 29));
8132 }
8133
8134 {
8135 auto date = Date(1999, 6, 30);
8136 date._addDays(1);
8137 assert(date == Date(1999, 7, 1));
8138 date._addDays(-1);
8139 assert(date == Date(1999, 6, 30));
8140 }
8141
8142 {
8143 auto date = Date(1999, 7, 31);
8144 date._addDays(1);
8145 assert(date == Date(1999, 8, 1));
8146 date._addDays(-1);
8147 assert(date == Date(1999, 7, 31));
8148 }
8149
8150 {
8151 auto date = Date(1999, 1, 1);
8152 date._addDays(-1);
8153 assert(date == Date(1998, 12, 31));
8154 date._addDays(1);
8155 assert(date == Date(1999, 1, 1));
8156 }
8157
8158 {
8159 auto date = Date(1999, 7, 6);
8160 date._addDays(9);
8161 assert(date == Date(1999, 7, 15));
8162 date._addDays(-11);
8163 assert(date == Date(1999, 7, 4));
8164 date._addDays(30);
8165 assert(date == Date(1999, 8, 3));
8166 date._addDays(-3);
8167 assert(date == Date(1999, 7, 31));
8168 }
8169
8170 {
8171 auto date = Date(1999, 7, 6);
8172 date._addDays(365);
8173 assert(date == Date(2000, 7, 5));
8174 date._addDays(-365);
8175 assert(date == Date(1999, 7, 6));
8176 date._addDays(366);
8177 assert(date == Date(2000, 7, 6));
8178 date._addDays(730);
8179 assert(date == Date(2002, 7, 6));
8180 date._addDays(-1096);
8181 assert(date == Date(1999, 7, 6));
8182 }
8183
8184 // Test B.C.
8185 {
8186 auto date = Date(-1999, 2, 28);
8187 date._addDays(1);
8188 assert(date == Date(-1999, 3, 1));
8189 date._addDays(-1);
8190 assert(date == Date(-1999, 2, 28));
8191 }
8192
8193 {
8194 auto date = Date(-2000, 2, 28);
8195 date._addDays(1);
8196 assert(date == Date(-2000, 2, 29));
8197 date._addDays(1);
8198 assert(date == Date(-2000, 3, 1));
8199 date._addDays(-1);
8200 assert(date == Date(-2000, 2, 29));
8201 }
8202
8203 {
8204 auto date = Date(-1999, 6, 30);
8205 date._addDays(1);
8206 assert(date == Date(-1999, 7, 1));
8207 date._addDays(-1);
8208 assert(date == Date(-1999, 6, 30));
8209 }
8210
8211 {
8212 auto date = Date(-1999, 7, 31);
8213 date._addDays(1);
8214 assert(date == Date(-1999, 8, 1));
8215 date._addDays(-1);
8216 assert(date == Date(-1999, 7, 31));
8217 }
8218
8219 {
8220 auto date = Date(-1999, 1, 1);
8221 date._addDays(-1);
8222 assert(date == Date(-2000, 12, 31));
8223 date._addDays(1);
8224 assert(date == Date(-1999, 1, 1));
8225 }
8226
8227 {
8228 auto date = Date(-1999, 7, 6);
8229 date._addDays(9);
8230 assert(date == Date(-1999, 7, 15));
8231 date._addDays(-11);
8232 assert(date == Date(-1999, 7, 4));
8233 date._addDays(30);
8234 assert(date == Date(-1999, 8, 3));
8235 date._addDays(-3);
8236 }
8237
8238 {
8239 auto date = Date(-1999, 7, 6);
8240 date._addDays(365);
8241 assert(date == Date(-1998, 7, 6));
8242 date._addDays(-365);
8243 assert(date == Date(-1999, 7, 6));
8244 date._addDays(366);
8245 assert(date == Date(-1998, 7, 7));
8246 date._addDays(730);
8247 assert(date == Date(-1996, 7, 6));
8248 date._addDays(-1096);
8249 assert(date == Date(-1999, 7, 6));
8250 }
8251
8252 // Test Both
8253 {
8254 auto date = Date(1, 7, 6);
8255 date._addDays(-365);
8256 assert(date == Date(0, 7, 6));
8257 date._addDays(365);
8258 assert(date == Date(1, 7, 6));
8259 date._addDays(-731);
8260 assert(date == Date(-1, 7, 6));
8261 date._addDays(730);
8262 assert(date == Date(1, 7, 5));
8263 }
8264
8265 const cdate = Date(1999, 7, 6);
8266 immutable idate = Date(1999, 7, 6);
8267 static assert(!__traits(compiles, cdate._addDays(12)));
8268 static assert(!__traits(compiles, idate._addDays(12)));
8269 }
8270
8271
8272 @safe pure invariant()
8273 {
8274 import std.format : format;
8275 assert(valid!"months"(_month),
8276 format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8277 assert(valid!"days"(_year, _month, _day),
8278 format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8279 }
8280
8281 short _year = 1;
8282 Month _month = Month.jan;
8283 ubyte _day = 1;
8284 }
8285
8286 ///
8287 @safe pure unittest
8288 {
8289 import core.time : days;
8290
8291 auto d = Date(2000, 6, 1);
8292
8293 assert(d.dayOfYear == 153);
8294 assert(d.dayOfWeek == DayOfWeek.thu);
8295
8296 d += 10.days;
8297 assert(d == Date(2000, 6, 11));
8298
8299 assert(d.toISOExtString() == "2000-06-11");
8300 assert(d.toISOString() == "20000611");
8301 assert(d.toSimpleString() == "2000-Jun-11");
8302
8303 assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
8304 assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
8305 assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
8306 }
8307
8308
8309 /++
8310 Represents a time of day with hours, minutes, and seconds. It uses 24 hour
8311 time.
8312 +/
8313 struct TimeOfDay
8314 {
8315 public:
8316
8317 /++
8318 Params:
8319 hour = Hour of the day [0 - 24$(RPAREN).
8320 minute = Minute of the hour [0 - 60$(RPAREN).
8321 second = Second of the minute [0 - 60$(RPAREN).
8322
8323 Throws:
8324 $(REF DateTimeException,std,datetime,date) if the resulting
8325 $(LREF TimeOfDay) would be not be valid.
8326 +/
8327 this(int hour, int minute, int second = 0) @safe pure
8328 {
8329 enforceValid!"hours"(hour);
8330 enforceValid!"minutes"(minute);
8331 enforceValid!"seconds"(second);
8332
8333 _hour = cast(ubyte) hour;
8334 _minute = cast(ubyte) minute;
8335 _second = cast(ubyte) second;
8336 }
8337
8338 @safe unittest
8339 {
8340 assert(TimeOfDay(0, 0) == TimeOfDay.init);
8341
8342 {
8343 auto tod = TimeOfDay(0, 0);
8344 assert(tod._hour == 0);
8345 assert(tod._minute == 0);
8346 assert(tod._second == 0);
8347 }
8348
8349 {
8350 auto tod = TimeOfDay(12, 30, 33);
8351 assert(tod._hour == 12);
8352 assert(tod._minute == 30);
8353 assert(tod._second == 33);
8354 }
8355
8356 {
8357 auto tod = TimeOfDay(23, 59, 59);
8358 assert(tod._hour == 23);
8359 assert(tod._minute == 59);
8360 assert(tod._second == 59);
8361 }
8362
8363 assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
8364 assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
8365 assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
8366 }
8367
8368
8369 /++
8370 Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
8371
8372 Returns:
8373 $(BOOKTABLE,
8374 $(TR $(TD this < rhs) $(TD < 0))
8375 $(TR $(TD this == rhs) $(TD 0))
8376 $(TR $(TD this > rhs) $(TD > 0))
8377 )
8378 +/
8379 int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
8380 {
8381 if (_hour < rhs._hour)
8382 return -1;
8383 if (_hour > rhs._hour)
8384 return 1;
8385
8386 if (_minute < rhs._minute)
8387 return -1;
8388 if (_minute > rhs._minute)
8389 return 1;
8390
8391 if (_second < rhs._second)
8392 return -1;
8393 if (_second > rhs._second)
8394 return 1;
8395
8396 return 0;
8397 }
8398
8399 @safe unittest
8400 {
8401 assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
8402
8403 assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
8404 assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
8405 assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
8406 assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8407
8408 assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
8409 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
8410
8411 assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
8412 assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8413
8414 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8415 assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8416 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
8417 assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8418 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
8419 assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
8420
8421 assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8422 assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
8423 assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
8424 assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8425
8426 assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8427 assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
8428
8429 const ctod = TimeOfDay(12, 30, 33);
8430 immutable itod = TimeOfDay(12, 30, 33);
8431 assert(ctod.opCmp(itod) == 0);
8432 assert(itod.opCmp(ctod) == 0);
8433 }
8434
8435
8436 /++
8437 Hours past midnight.
8438 +/
8439 @property ubyte hour() const @safe pure nothrow @nogc
8440 {
8441 return _hour;
8442 }
8443
8444 @safe unittest
8445 {
8446 assert(TimeOfDay.init.hour == 0);
8447 assert(TimeOfDay(12, 0, 0).hour == 12);
8448
8449 const ctod = TimeOfDay(12, 0, 0);
8450 immutable itod = TimeOfDay(12, 0, 0);
8451 assert(ctod.hour == 12);
8452 assert(itod.hour == 12);
8453 }
8454
8455
8456 /++
8457 Hours past midnight.
8458
8459 Params:
8460 hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
8461
8462 Throws:
8463 $(REF DateTimeException,std,datetime,date) if the given hour would
8464 result in an invalid $(LREF TimeOfDay).
8465 +/
8466 @property void hour(int hour) @safe pure
8467 {
8468 enforceValid!"hours"(hour);
8469 _hour = cast(ubyte) hour;
8470 }
8471
8472 @safe unittest
8473 {
8474 assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
8475
8476 auto tod = TimeOfDay(0, 0, 0);
8477 tod.hour = 12;
8478 assert(tod == TimeOfDay(12, 0, 0));
8479
8480 const ctod = TimeOfDay(0, 0, 0);
8481 immutable itod = TimeOfDay(0, 0, 0);
8482 static assert(!__traits(compiles, ctod.hour = 12));
8483 static assert(!__traits(compiles, itod.hour = 12));
8484 }
8485
8486
8487 /++
8488 Minutes past the hour.
8489 +/
8490 @property ubyte minute() const @safe pure nothrow @nogc
8491 {
8492 return _minute;
8493 }
8494
8495 @safe unittest
8496 {
8497 assert(TimeOfDay.init.minute == 0);
8498 assert(TimeOfDay(0, 30, 0).minute == 30);
8499
8500 const ctod = TimeOfDay(0, 30, 0);
8501 immutable itod = TimeOfDay(0, 30, 0);
8502 assert(ctod.minute == 30);
8503 assert(itod.minute == 30);
8504 }
8505
8506
8507 /++
8508 Minutes past the hour.
8509
8510 Params:
8511 minute = The minute to set this $(LREF TimeOfDay)'s minute to.
8512
8513 Throws:
8514 $(REF DateTimeException,std,datetime,date) if the given minute
8515 would result in an invalid $(LREF TimeOfDay).
8516 +/
8517 @property void minute(int minute) @safe pure
8518 {
8519 enforceValid!"minutes"(minute);
8520 _minute = cast(ubyte) minute;
8521 }
8522
8523 @safe unittest
8524 {
8525 assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
8526
8527 auto tod = TimeOfDay(0, 0, 0);
8528 tod.minute = 30;
8529 assert(tod == TimeOfDay(0, 30, 0));
8530
8531 const ctod = TimeOfDay(0, 0, 0);
8532 immutable itod = TimeOfDay(0, 0, 0);
8533 static assert(!__traits(compiles, ctod.minute = 30));
8534 static assert(!__traits(compiles, itod.minute = 30));
8535 }
8536
8537
8538 /++
8539 Seconds past the minute.
8540 +/
8541 @property ubyte second() const @safe pure nothrow @nogc
8542 {
8543 return _second;
8544 }
8545
8546 @safe unittest
8547 {
8548 assert(TimeOfDay.init.second == 0);
8549 assert(TimeOfDay(0, 0, 33).second == 33);
8550
8551 const ctod = TimeOfDay(0, 0, 33);
8552 immutable itod = TimeOfDay(0, 0, 33);
8553 assert(ctod.second == 33);
8554 assert(itod.second == 33);
8555 }
8556
8557
8558 /++
8559 Seconds past the minute.
8560
8561 Params:
8562 second = The second to set this $(LREF TimeOfDay)'s second to.
8563
8564 Throws:
8565 $(REF DateTimeException,std,datetime,date) if the given second
8566 would result in an invalid $(LREF TimeOfDay).
8567 +/
8568 @property void second(int second) @safe pure
8569 {
8570 enforceValid!"seconds"(second);
8571 _second = cast(ubyte) second;
8572 }
8573
8574 @safe unittest
8575 {
8576 assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
8577
8578 auto tod = TimeOfDay(0, 0, 0);
8579 tod.second = 33;
8580 assert(tod == TimeOfDay(0, 0, 33));
8581
8582 const ctod = TimeOfDay(0, 0, 0);
8583 immutable itod = TimeOfDay(0, 0, 0);
8584 static assert(!__traits(compiles, ctod.second = 33));
8585 static assert(!__traits(compiles, itod.second = 33));
8586 }
8587
8588
8589 /++
8590 Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
8591 negative number will subtract.
8592
8593 The difference between rolling and adding is that rolling does not
8594 affect larger units. For instance, rolling a $(LREF TimeOfDay)
8595 one hours's worth of minutes gets the exact same
8596 $(LREF TimeOfDay).
8597
8598 Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
8599
8600 Params:
8601 units = The units to add.
8602 value = The number of $(D_PARAM units) to add to this
8603 $(LREF TimeOfDay).
8604
8605 Returns:
8606 A reference to the `TimeOfDay` (`this`).
8607 +/
8608 ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8609 if (units == "hours")
8610 {
8611 import core.time : dur;
8612 return this += dur!"hours"(value);
8613 }
8614
8615 ///
8616 @safe unittest
8617 {
8618 auto tod1 = TimeOfDay(7, 12, 0);
8619 tod1.roll!"hours"(1);
8620 assert(tod1 == TimeOfDay(8, 12, 0));
8621
8622 auto tod2 = TimeOfDay(7, 12, 0);
8623 tod2.roll!"hours"(-1);
8624 assert(tod2 == TimeOfDay(6, 12, 0));
8625
8626 auto tod3 = TimeOfDay(23, 59, 0);
8627 tod3.roll!"minutes"(1);
8628 assert(tod3 == TimeOfDay(23, 0, 0));
8629
8630 auto tod4 = TimeOfDay(0, 0, 0);
8631 tod4.roll!"minutes"(-1);
8632 assert(tod4 == TimeOfDay(0, 59, 0));
8633
8634 auto tod5 = TimeOfDay(23, 59, 59);
8635 tod5.roll!"seconds"(1);
8636 assert(tod5 == TimeOfDay(23, 59, 0));
8637
8638 auto tod6 = TimeOfDay(0, 0, 0);
8639 tod6.roll!"seconds"(-1);
8640 assert(tod6 == TimeOfDay(0, 0, 59));
8641 }
8642
8643 @safe unittest
8644 {
8645 auto tod = TimeOfDay(12, 27, 2);
8646 tod.roll!"hours"(22).roll!"hours"(-7);
8647 assert(tod == TimeOfDay(3, 27, 2));
8648
8649 const ctod = TimeOfDay(0, 0, 0);
8650 immutable itod = TimeOfDay(0, 0, 0);
8651 static assert(!__traits(compiles, ctod.roll!"hours"(53)));
8652 static assert(!__traits(compiles, itod.roll!"hours"(53)));
8653 }
8654
8655
8656 /// ditto
8657 ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8658 if (units == "minutes" || units == "seconds")
8659 {
8660 import std.format : format;
8661
8662 enum memberVarStr = units[0 .. $ - 1];
8663 value %= 60;
8664 mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
8665
8666 if (value < 0)
8667 {
8668 if (newVal < 0)
8669 newVal += 60;
8670 }
8671 else if (newVal >= 60)
8672 newVal -= 60;
8673
8674 mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
8675 return this;
8676 }
8677
8678 // Test roll!"minutes"().
8679 @safe unittest
8680 {
8681 static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
8682 {
8683 orig.roll!"minutes"(minutes);
8684 assert(orig == expected);
8685 }
8686
8687 testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8688 testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
8689 testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
8690 testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
8691 testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
8692 testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
8693 testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
8694 testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
8695 testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
8696 testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
8697 testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
8698 testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8699 testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
8700 testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
8701 testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
8702
8703 testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
8704 testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
8705 testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
8706 testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
8707 testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
8708 testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
8709 testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
8710 testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
8711
8712 testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
8713 testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
8714 testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
8715 testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
8716 testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
8717 testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
8718 testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
8719 testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
8720 testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
8721 testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
8722 testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8723 testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
8724 testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
8725 testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
8726
8727 testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
8728 testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
8729 testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
8730 testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
8731 testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
8732 testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
8733 testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
8734 testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
8735
8736 testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
8737 testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
8738 testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
8739
8740 testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
8741 testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
8742 testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
8743
8744 testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
8745 testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
8746 testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
8747
8748 testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
8749 testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
8750 testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
8751
8752 auto tod = TimeOfDay(12, 27, 2);
8753 tod.roll!"minutes"(97).roll!"minutes"(-102);
8754 assert(tod == TimeOfDay(12, 22, 2));
8755
8756 const ctod = TimeOfDay(0, 0, 0);
8757 immutable itod = TimeOfDay(0, 0, 0);
8758 static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
8759 static assert(!__traits(compiles, itod.roll!"minutes"(7)));
8760 }
8761
8762 // Test roll!"seconds"().
8763 @safe unittest
8764 {
8765 static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
8766 {
8767 orig.roll!"seconds"(seconds);
8768 assert(orig == expected);
8769 }
8770
8771 testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8772 testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
8773 testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
8774 testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
8775 testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
8776 testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
8777 testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
8778 testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
8779 testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
8780 testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
8781 testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
8782 testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
8783 testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8784 testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
8785
8786 testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
8787 testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
8788 testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
8789 testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
8790 testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
8791 testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
8792 testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
8793 testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
8794
8795 testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
8796 testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
8797 testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
8798 testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
8799 testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
8800 testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
8801 testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
8802 testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
8803 testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
8804 testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
8805 testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
8806 testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8807 testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
8808
8809 testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
8810 testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
8811 testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
8812
8813 testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
8814 testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
8815 testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
8816
8817 testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
8818 testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
8819 testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
8820
8821 testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
8822 testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
8823 testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
8824
8825 auto tod = TimeOfDay(12, 27, 2);
8826 tod.roll!"seconds"(105).roll!"seconds"(-77);
8827 assert(tod == TimeOfDay(12, 27, 30));
8828
8829 const ctod = TimeOfDay(0, 0, 0);
8830 immutable itod = TimeOfDay(0, 0, 0);
8831 static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
8832 static assert(!__traits(compiles, itod.roll!"seconds"(7)));
8833 }
8834
8835
8836 import core.time : Duration;
8837 /++
8838 Gives the result of adding or subtracting a $(REF Duration, core,time)
8839 from this $(LREF TimeOfDay).
8840
8841 The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8842 are
8843
8844 $(BOOKTABLE,
8845 $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8846 $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8847 )
8848
8849 Params:
8850 duration = The $(REF Duration, core,time) to add to or subtract from
8851 this $(LREF TimeOfDay).
8852 +/
8853 TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
8854 if (op == "+" || op == "-")
8855 {
8856 TimeOfDay retval = this;
8857 immutable seconds = duration.total!"seconds";
8858 mixin("return retval._addSeconds(" ~ op ~ "seconds);");
8859 }
8860
8861 ///
8862 @safe unittest
8863 {
8864 import core.time : hours, minutes, seconds;
8865
8866 assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
8867 assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
8868 assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
8869 assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
8870
8871 assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
8872 assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
8873 assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
8874 assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
8875 }
8876
8877 @safe unittest
8878 {
8879 auto tod = TimeOfDay(12, 30, 33);
8880
8881 import core.time : dur;
8882 assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8883 assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8884 assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8885 assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8886 assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8887 assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8888
8889 assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8890 assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8891 assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8892 assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8893 assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8894 assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8895
8896 assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8897 assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8898 assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8899 assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8900 assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8901 assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8902
8903 assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8904 assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8905 assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8906 assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8907 assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8908 assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8909
8910 auto duration = dur!"hours"(11);
8911 const ctod = TimeOfDay(12, 30, 33);
8912 immutable itod = TimeOfDay(12, 30, 33);
8913 assert(tod + duration == TimeOfDay(23, 30, 33));
8914 assert(ctod + duration == TimeOfDay(23, 30, 33));
8915 assert(itod + duration == TimeOfDay(23, 30, 33));
8916
8917 assert(tod - duration == TimeOfDay(1, 30, 33));
8918 assert(ctod - duration == TimeOfDay(1, 30, 33));
8919 assert(itod - duration == TimeOfDay(1, 30, 33));
8920 }
8921
8922
8923 /++
8924 Gives the result of adding or subtracting a $(REF Duration, core,time)
8925 from this $(LREF TimeOfDay), as well as assigning the result to this
8926 $(LREF TimeOfDay).
8927
8928 The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8929 are
8930
8931 $(BOOKTABLE,
8932 $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8933 $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8934 )
8935
8936 Params:
8937 duration = The $(REF Duration, core,time) to add to or subtract from
8938 this $(LREF TimeOfDay).
8939 +/
8940 ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
8941 if (op == "+" || op == "-")
8942 {
8943 immutable seconds = duration.total!"seconds";
8944 mixin("return _addSeconds(" ~ op ~ "seconds);");
8945 }
8946
8947 @safe unittest
8948 {
8949 import core.time : dur;
8950 auto duration = dur!"hours"(12);
8951
8952 assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8953 assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8954 assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8955 assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8956 assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8957 assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8958
8959 assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8960 assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8961 assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8962 assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8963 assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8964 assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8965
8966 assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8967 assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8968 assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8969 assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8970 assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8971 assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8972
8973 assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8974 assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8975 assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8976 assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8977 assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8978 assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8979
8980 auto tod = TimeOfDay(19, 17, 22);
8981 (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
8982 assert(tod == TimeOfDay(17, 15, 59));
8983
8984 const ctod = TimeOfDay(12, 33, 30);
8985 immutable itod = TimeOfDay(12, 33, 30);
8986 static assert(!__traits(compiles, ctod += duration));
8987 static assert(!__traits(compiles, itod += duration));
8988 static assert(!__traits(compiles, ctod -= duration));
8989 static assert(!__traits(compiles, itod -= duration));
8990 }
8991
8992
8993 /++
8994 Gives the difference between two $(LREF TimeOfDay)s.
8995
8996 The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8997 are
8998
8999 $(BOOKTABLE,
9000 $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
9001 )
9002
9003 Params:
9004 rhs = The $(LREF TimeOfDay) to subtract from this one.
9005 +/
9006 Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
9007 if (op == "-")
9008 {
9009 immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
9010 immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
9011
9012 import core.time : dur;
9013 return dur!"seconds"(lhsSec - rhsSec);
9014 }
9015
9016 @safe unittest
9017 {
9018 auto tod = TimeOfDay(12, 30, 33);
9019
9020 import core.time : dur;
9021 assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
9022 assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
9023 assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
9024 assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
9025 assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
9026 assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
9027 assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
9028 assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
9029
9030 const ctod = TimeOfDay(12, 30, 33);
9031 immutable itod = TimeOfDay(12, 30, 33);
9032 assert(tod - tod == Duration.zero);
9033 assert(ctod - tod == Duration.zero);
9034 assert(itod - tod == Duration.zero);
9035
9036 assert(tod - ctod == Duration.zero);
9037 assert(ctod - ctod == Duration.zero);
9038 assert(itod - ctod == Duration.zero);
9039
9040 assert(tod - itod == Duration.zero);
9041 assert(ctod - itod == Duration.zero);
9042 assert(itod - itod == Duration.zero);
9043 }
9044
9045
9046 /++
9047 Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
9048 If `writer` is set, the resulting string will be written directly to it.
9049
9050 Params:
9051 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9052 Returns:
9053 A `string` when not using an output range; `void` otherwise.
9054 +/
9055 string toISOString() const @safe pure nothrow
9056 {
9057 import std.array : appender;
9058 auto w = appender!string();
9059 w.reserve(6);
9060 try
9061 toISOString(w);
9062 catch (Exception e)
9063 assert(0, "toISOString() threw.");
9064 return w.data;
9065 }
9066
9067 /// ditto
9068 void toISOString(W)(ref W writer) const
9069 if (isOutputRange!(W, char))
9070 {
9071 import std.format.write : formattedWrite;
9072 formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
9073 }
9074
9075 ///
9076 @safe unittest
9077 {
9078 assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
9079 assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
9080 }
9081
9082 @safe unittest
9083 {
9084 auto tod = TimeOfDay(12, 30, 33);
9085 const ctod = TimeOfDay(12, 30, 33);
9086 immutable itod = TimeOfDay(12, 30, 33);
9087 assert(tod.toISOString() == "123033");
9088 assert(ctod.toISOString() == "123033");
9089 assert(itod.toISOString() == "123033");
9090 }
9091
9092
9093 /++
9094 Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
9095 If `writer` is set, the resulting string will be written directly to it.
9096
9097 Params:
9098 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9099 Returns:
9100 A `string` when not using an output range; `void` otherwise.
9101 +/
9102 string toISOExtString() const @safe pure nothrow
9103 {
9104 import std.array : appender;
9105 auto w = appender!string();
9106 w.reserve(8);
9107 try
9108 toISOExtString(w);
9109 catch (Exception e)
9110 assert(0, "toISOExtString() threw.");
9111 return w.data;
9112 }
9113
9114 /// ditto
9115 void toISOExtString(W)(ref W writer) const
9116 if (isOutputRange!(W, char))
9117 {
9118 import std.format.write : formattedWrite;
9119 formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
9120 }
9121
9122 ///
9123 @safe unittest
9124 {
9125 assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
9126 assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
9127 }
9128
9129 @safe unittest
9130 {
9131 auto tod = TimeOfDay(12, 30, 33);
9132 const ctod = TimeOfDay(12, 30, 33);
9133 immutable itod = TimeOfDay(12, 30, 33);
9134 assert(tod.toISOExtString() == "12:30:33");
9135 assert(ctod.toISOExtString() == "12:30:33");
9136 assert(itod.toISOExtString() == "12:30:33");
9137 }
9138
9139
9140 /++
9141 Converts this TimeOfDay to a string.
9142
9143 This function exists to make it easy to convert a $(LREF TimeOfDay) to a
9144 string for code that does not care what the exact format is - just that
9145 it presents the information in a clear manner. It also makes it easy to
9146 simply convert a $(LREF TimeOfDay) to a string when using functions such
9147 as `to!string`, `format`, or `writeln` which use toString to convert
9148 user-defined types. So, it is unlikely that much code will call
9149 toString directly.
9150
9151 The format of the string is purposefully unspecified, and code that
9152 cares about the format of the string should use `toISOString`,
9153 `toISOExtString`, or some other custom formatting function that
9154 explicitly generates the format that the code needs. The reason is that
9155 the code is then clear about what format it's using, making it less
9156 error-prone to maintain the code and interact with other software that
9157 consumes the generated strings. It's for this same reason that
9158 $(LREF TimeOfDay) has no `fromString` function, whereas it does have
9159 `fromISOString` and `fromISOExtString`.
9160
9161 The format returned by toString may or may not change in the future.
9162
9163 Params:
9164 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9165 Returns:
9166 A `string` when not using an output range; `void` otherwise.
9167 +/
9168 string toString() const @safe pure nothrow
9169 {
9170 return toISOExtString();
9171 }
9172
9173 /// ditto
9174 void toString(W)(ref W writer) const
9175 if (isOutputRange!(W, char))
9176 {
9177 toISOExtString(writer);
9178 }
9179
9180 @safe unittest
9181 {
9182 auto tod = TimeOfDay(12, 30, 33);
9183 const ctod = TimeOfDay(12, 30, 33);
9184 immutable itod = TimeOfDay(12, 30, 33);
9185 assert(tod.toString());
9186 assert(ctod.toString());
9187 assert(itod.toString());
9188 }
9189
9190
9191 /++
9192 Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
9193 Whitespace is stripped from the given string.
9194
9195 Params:
9196 isoString = A string formatted in the ISO format for times.
9197
9198 Throws:
9199 $(REF DateTimeException,std,datetime,date) if the given string is
9200 not in the ISO format or if the resulting $(LREF TimeOfDay) would
9201 not be valid.
9202 +/
9203 static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
9204 if (isSomeString!S)
9205 {
9206 import std.conv : to, text, ConvException;
9207 import std.exception : enforce;
9208 import std.string : strip;
9209
9210 int hours, minutes, seconds;
9211 auto str = strip(isoString);
9212
9213 enforce!DateTimeException(str.length == 6, text("Invalid format for TimeOfDay.fromISOString: ", isoString));
9214
9215 try
9216 {
9217 // cast to int from uint is used because it checks for
9218 // non digits without extra loops
9219 hours = cast(int) to!uint(str[0 .. 2]);
9220 minutes = cast(int) to!uint(str[2 .. 4]);
9221 seconds = cast(int) to!uint(str[4 .. $]);
9222 }
9223 catch (ConvException)
9224 {
9225 throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOString: ", isoString));
9226 }
9227
9228 return TimeOfDay(hours, minutes, seconds);
9229 }
9230
9231 ///
9232 @safe unittest
9233 {
9234 assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
9235 assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
9236 assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
9237 }
9238
9239 @safe unittest
9240 {
9241 assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
9242 assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
9243 assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
9244 assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
9245 assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
9246 assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
9247 assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
9248 assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
9249 assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
9250 assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
9251 assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
9252 assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
9253 assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
9254 assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
9255 assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
9256 assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
9257 assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
9258 assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
9259 assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
9260 assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
9261 assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
9262 assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
9263
9264 assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
9265 assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
9266 assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
9267 assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
9268 assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
9269 assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
9270 assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
9271 assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
9272 assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
9273 assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
9274 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
9275 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
9276 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
9277 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
9278 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
9279 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
9280 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
9281 assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
9282 assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
9283 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
9284 assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
9285 assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
9286 assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
9287 assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
9288 assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
9289 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
9290 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
9291
9292 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
9293
9294 assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
9295 assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
9296 assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
9297 assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
9298 assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
9299 assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
9300 }
9301
9302 // https://issues.dlang.org/show_bug.cgi?id=17801
9303 @safe unittest
9304 {
9305 import std.conv : to;
9306 import std.meta : AliasSeq;
9307 static foreach (C; AliasSeq!(char, wchar, dchar))
9308 {
9309 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9310 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
9311 }
9312 }
9313
9314
9315 /++
9316 Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
9317 Whitespace is stripped from the given string.
9318
9319 Params:
9320 isoExtString = A string formatted in the ISO Extended format for
9321 times.
9322
9323 Throws:
9324 $(REF DateTimeException,std,datetime,date) if the given string is
9325 not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
9326 would not be valid.
9327 +/
9328 static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
9329 if (isSomeString!S)
9330 {
9331 import std.conv : ConvException, text, to;
9332 import std.string : strip;
9333
9334 auto str = strip(isoExtString);
9335 int hours, minutes, seconds;
9336
9337 if (str.length != 8 || str[2] != ':' || str[5] != ':')
9338 throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOExtString: ", isoExtString));
9339
9340 try
9341 {
9342 // cast to int from uint is used because it checks for
9343 // non digits without extra loops
9344 hours = cast(int) to!uint(str[0 .. 2]);
9345 minutes = cast(int) to!uint(str[3 .. 5]);
9346 seconds = cast(int) to!uint(str[6 .. $]);
9347 }
9348 catch (ConvException)
9349 {
9350 throw new DateTimeException(text("Invalid format for TimeOfDay.fromISOExtString: ", isoExtString));
9351 }
9352
9353 return TimeOfDay(hours, minutes, seconds);
9354 }
9355
9356 ///
9357 @safe unittest
9358 {
9359 assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
9360 assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
9361 assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
9362 }
9363
9364 @safe unittest
9365 {
9366 assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
9367 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
9368 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
9369 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
9370 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
9371 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
9372 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
9373 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
9374 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
9375 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
9376 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
9377 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
9378 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
9379 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
9380 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
9381 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
9382 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
9383 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
9384 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
9385 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
9386 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
9387 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
9388
9389 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
9390 assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
9391 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
9392 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
9393 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
9394 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
9395 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
9396 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
9397 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
9398 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
9399 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
9400 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
9401 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
9402 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
9403 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
9404 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
9405 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
9406 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
9407 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
9408 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
9409 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
9410 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
9411 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
9412 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
9413 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
9414 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
9415 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
9416
9417 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
9418
9419 assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
9420 assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
9421 assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
9422 assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
9423 assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
9424 assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
9425 }
9426
9427 // https://issues.dlang.org/show_bug.cgi?id=17801
9428 @safe unittest
9429 {
9430 import std.conv : to;
9431 import std.meta : AliasSeq;
9432 static foreach (C; AliasSeq!(char, wchar, dchar))
9433 {
9434 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
9435 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
9436 }
9437 }
9438
9439
9440 /++
9441 Returns midnight.
9442 +/
9443 @property static TimeOfDay min() @safe pure nothrow @nogc
9444 {
9445 return TimeOfDay.init;
9446 }
9447
9448 @safe unittest
9449 {
9450 assert(TimeOfDay.min.hour == 0);
9451 assert(TimeOfDay.min.minute == 0);
9452 assert(TimeOfDay.min.second == 0);
9453 assert(TimeOfDay.min < TimeOfDay.max);
9454 }
9455
9456
9457 /++
9458 Returns one second short of midnight.
9459 +/
9460 @property static TimeOfDay max() @safe pure nothrow @nogc
9461 {
9462 auto tod = TimeOfDay.init;
9463 tod._hour = maxHour;
9464 tod._minute = maxMinute;
9465 tod._second = maxSecond;
9466
9467 return tod;
9468 }
9469
9470 @safe unittest
9471 {
9472 assert(TimeOfDay.max.hour == 23);
9473 assert(TimeOfDay.max.minute == 59);
9474 assert(TimeOfDay.max.second == 59);
9475 assert(TimeOfDay.max > TimeOfDay.min);
9476 }
9477
9478
9479 private:
9480
9481 /+
9482 Add seconds to the time of day. Negative values will subtract. If the
9483 number of seconds overflows (or underflows), then the seconds will wrap,
9484 increasing (or decreasing) the number of minutes accordingly. If the
9485 number of minutes overflows (or underflows), then the minutes will wrap.
9486 If the number of minutes overflows(or underflows), then the hour will
9487 wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
9488
9489 Params:
9490 seconds = The number of seconds to add to this TimeOfDay.
9491 +/
9492 ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
9493 {
9494 import core.time : convert;
9495 long hnsecs = convert!("seconds", "hnsecs")(seconds);
9496 hnsecs += convert!("hours", "hnsecs")(_hour);
9497 hnsecs += convert!("minutes", "hnsecs")(_minute);
9498 hnsecs += convert!("seconds", "hnsecs")(_second);
9499
9500 hnsecs %= convert!("days", "hnsecs")(1);
9501
9502 if (hnsecs < 0)
9503 hnsecs += convert!("days", "hnsecs")(1);
9504
9505 immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
9506 immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9507 immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
9508
9509 _hour = cast(ubyte) newHours;
9510 _minute = cast(ubyte) newMinutes;
9511 _second = cast(ubyte) newSeconds;
9512
9513 return this;
9514 }
9515
9516 @safe unittest
9517 {
9518 static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
9519 {
9520 orig._addSeconds(seconds);
9521 assert(orig == expected);
9522 }
9523
9524 testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
9525 testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
9526 testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
9527 testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
9528 testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
9529 testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
9530 testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
9531 testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
9532 testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
9533 testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
9534 testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
9535 testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
9536 testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
9537 testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
9538
9539 testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
9540 testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
9541 testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
9542 testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
9543 testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
9544 testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
9545 testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
9546 testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
9547
9548 testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
9549 testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
9550 testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
9551 testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
9552 testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
9553 testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
9554 testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
9555 testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
9556 testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
9557 testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
9558 testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
9559 testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
9560 testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
9561
9562 testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
9563 testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
9564 testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
9565 testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
9566 testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
9567 testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
9568
9569 testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
9570 testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
9571 testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
9572
9573 testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
9574 testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
9575 testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
9576
9577 testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
9578 testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
9579 testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
9580
9581 testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
9582 testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
9583 testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
9584
9585 const ctod = TimeOfDay(0, 0, 0);
9586 immutable itod = TimeOfDay(0, 0, 0);
9587 static assert(!__traits(compiles, ctod._addSeconds(7)));
9588 static assert(!__traits(compiles, itod._addSeconds(7)));
9589 }
9590
9591
9592 /+
9593 Whether the given values form a valid $(LREF TimeOfDay).
9594 +/
9595 static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
9596 {
9597 return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
9598 }
9599
9600
9601 @safe pure invariant()
9602 {
9603 import std.format : format;
9604 assert(_valid(_hour, _minute, _second),
9605 format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
9606 }
9607
9608
9609 package:
9610
9611 ubyte _hour;
9612 ubyte _minute;
9613 ubyte _second;
9614
9615 enum ubyte maxHour = 24 - 1;
9616 enum ubyte maxMinute = 60 - 1;
9617 enum ubyte maxSecond = 60 - 1;
9618 }
9619
9620 ///
9621 @safe pure unittest
9622 {
9623 import core.time : minutes, seconds;
9624
9625 auto t = TimeOfDay(12, 30, 0);
9626
9627 t += 10.minutes + 100.seconds;
9628 assert(t == TimeOfDay(12, 41, 40));
9629
9630 assert(t.toISOExtString() == "12:41:40");
9631 assert(t.toISOString() == "124140");
9632
9633 assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
9634 assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
9635 }
9636
9637 /++
9638 Returns whether the given value is valid for the given unit type when in a
9639 time point. Naturally, a duration is not held to a particular range, but
9640 the values in a time point are (e.g. a month must be in the range of
9641 1 - 12 inclusive).
9642
9643 Params:
9644 units = The units of time to validate.
9645 value = The number to validate.
9646 +/
9647 bool valid(string units)(int value) @safe pure nothrow @nogc
9648 if (units == "months" ||
9649 units == "hours" ||
9650 units == "minutes" ||
9651 units == "seconds")
9652 {
9653 static if (units == "months")
9654 return value >= Month.jan && value <= Month.dec;
9655 else static if (units == "hours")
9656 return value >= 0 && value <= 23;
9657 else static if (units == "minutes")
9658 return value >= 0 && value <= 59;
9659 else static if (units == "seconds")
9660 return value >= 0 && value <= 59;
9661 }
9662
9663 ///
9664 @safe unittest
9665 {
9666 assert(valid!"hours"(12));
9667 assert(!valid!"hours"(32));
9668 assert(valid!"months"(12));
9669 assert(!valid!"months"(13));
9670 }
9671
9672 /++
9673 Returns whether the given day is valid for the given year and month.
9674
9675 Params:
9676 units = The units of time to validate.
9677 year = The year of the day to validate.
9678 month = The month of the day to validate (January is 1).
9679 day = The day to validate.
9680 +/
9681 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9682 if (units == "days")
9683 {
9684 return day > 0 && day <= maxDay(year, month);
9685 }
9686
9687 ///
9688 @safe pure nothrow @nogc unittest
9689 {
9690 assert(valid!"days"(2016, 2, 29));
9691 assert(!valid!"days"(2016, 2, 30));
9692 assert(valid!"days"(2017, 2, 20));
9693 assert(!valid!"days"(2017, 2, 29));
9694 }
9695
9696 private short castToYear(int year, string file = __FILE__, size_t line = __LINE__) @safe pure
9697 {
9698 import std.conv : to, ConvOverflowException;
9699 import std.format : format;
9700
9701 try
9702 return year.to!short;
9703 catch (ConvOverflowException)
9704 throw new DateTimeException(format("year %s doesn't fit to Date.", year), file, line);
9705 }
9706
9707 /++
9708 Params:
9709 units = The units of time to validate.
9710 value = The number to validate.
9711 file = The file that the $(LREF DateTimeException) will list if thrown.
9712 line = The line number that the $(LREF DateTimeException) will list if
9713 thrown.
9714
9715 Throws:
9716 $(LREF DateTimeException) if `valid!units(value)` is false.
9717 +/
9718 void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9719 if (units == "months" ||
9720 units == "hours" ||
9721 units == "minutes" ||
9722 units == "seconds")
9723 {
9724 import std.format : format;
9725
9726 static if (units == "months")
9727 {
9728 if (!valid!units(value))
9729 throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
9730 }
9731 else static if (units == "hours")
9732 {
9733 if (!valid!units(value))
9734 throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
9735 }
9736 else static if (units == "minutes")
9737 {
9738 if (!valid!units(value))
9739 throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
9740 }
9741 else static if (units == "seconds")
9742 {
9743 if (!valid!units(value))
9744 throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
9745 }
9746 }
9747
9748 ///
9749 @safe pure unittest
9750 {
9751 import std.exception : assertThrown, assertNotThrown;
9752
9753 assertNotThrown(enforceValid!"months"(10));
9754 assertNotThrown(enforceValid!"seconds"(40));
9755
9756 assertThrown!DateTimeException(enforceValid!"months"(0));
9757 assertThrown!DateTimeException(enforceValid!"hours"(24));
9758 assertThrown!DateTimeException(enforceValid!"minutes"(60));
9759 assertThrown!DateTimeException(enforceValid!"seconds"(60));
9760 }
9761
9762
9763 /++
9764 Because the validity of the day number depends on both on the year
9765 and month of which the day is occurring, take all three variables
9766 to validate the day.
9767
9768 Params:
9769 units = The units of time to validate.
9770 year = The year of the day to validate.
9771 month = The month of the day to validate.
9772 day = The day to validate.
9773 file = The file that the $(LREF DateTimeException) will list if thrown.
9774 line = The line number that the $(LREF DateTimeException) will list if
9775 thrown.
9776
9777 Throws:
9778 $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
9779 +/
9780 void enforceValid(string units)
9781 (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9782 if (units == "days")
9783 {
9784 import std.format : format;
9785 if (!valid!"days"(year, month, day))
9786 throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
9787 }
9788
9789 ///
9790 @safe pure unittest
9791 {
9792 import std.exception : assertThrown, assertNotThrown;
9793
9794 assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
9795 // leap year
9796 assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
9797
9798 assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
9799 assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
9800 assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
9801 }
9802
9803
9804 /++
9805 Returns the number of days from the current day of the week to the given
9806 day of the week. If they are the same, then the result is 0.
9807
9808 Params:
9809 currDoW = The current day of the week.
9810 dow = The day of the week to get the number of days to.
9811 +/
9812 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
9813 {
9814 if (currDoW == dow)
9815 return 0;
9816 if (currDoW < dow)
9817 return dow - currDoW;
9818 return DayOfWeek.sat - currDoW + dow + 1;
9819 }
9820
9821 ///
9822 @safe pure nothrow @nogc unittest
9823 {
9824 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9825 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9826 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9827 }
9828
9829 @safe unittest
9830 {
9831 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
9832 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
9833 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
9834 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
9835 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
9836 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
9837 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
9838
9839 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9840 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9841 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
9842 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9843 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
9844 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
9845 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
9846
9847 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
9848 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
9849 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
9850 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
9851 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
9852 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
9853 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
9854
9855 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
9856 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
9857 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
9858 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
9859 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
9860 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
9861 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
9862
9863 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
9864 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
9865 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
9866 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
9867 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
9868 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
9869 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
9870
9871 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
9872 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
9873 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
9874 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
9875 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
9876 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
9877 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
9878
9879 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
9880 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
9881 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
9882 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
9883 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
9884 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
9885 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
9886 }
9887
9888
9889 /++
9890 Returns the number of months from the current months of the year to the
9891 given month of the year. If they are the same, then the result is 0.
9892
9893 Params:
9894 currMonth = The current month of the year.
9895 month = The month of the year to get the number of months to.
9896 +/
9897 int monthsToMonth(int currMonth, int month) @safe pure
9898 {
9899 enforceValid!"months"(currMonth);
9900 enforceValid!"months"(month);
9901
9902 if (currMonth == month)
9903 return 0;
9904 if (currMonth < month)
9905 return month - currMonth;
9906 return Month.dec - currMonth + month;
9907 }
9908
9909 ///
9910 @safe pure unittest
9911 {
9912 assert(monthsToMonth(Month.jan, Month.jan) == 0);
9913 assert(monthsToMonth(Month.jan, Month.dec) == 11);
9914 assert(monthsToMonth(Month.jul, Month.oct) == 3);
9915 }
9916
9917 @safe unittest
9918 {
9919 assert(monthsToMonth(Month.jan, Month.jan) == 0);
9920 assert(monthsToMonth(Month.jan, Month.feb) == 1);
9921 assert(monthsToMonth(Month.jan, Month.mar) == 2);
9922 assert(monthsToMonth(Month.jan, Month.apr) == 3);
9923 assert(monthsToMonth(Month.jan, Month.may) == 4);
9924 assert(monthsToMonth(Month.jan, Month.jun) == 5);
9925 assert(monthsToMonth(Month.jan, Month.jul) == 6);
9926 assert(monthsToMonth(Month.jan, Month.aug) == 7);
9927 assert(monthsToMonth(Month.jan, Month.sep) == 8);
9928 assert(monthsToMonth(Month.jan, Month.oct) == 9);
9929 assert(monthsToMonth(Month.jan, Month.nov) == 10);
9930 assert(monthsToMonth(Month.jan, Month.dec) == 11);
9931
9932 assert(monthsToMonth(Month.may, Month.jan) == 8);
9933 assert(monthsToMonth(Month.may, Month.feb) == 9);
9934 assert(monthsToMonth(Month.may, Month.mar) == 10);
9935 assert(monthsToMonth(Month.may, Month.apr) == 11);
9936 assert(monthsToMonth(Month.may, Month.may) == 0);
9937 assert(monthsToMonth(Month.may, Month.jun) == 1);
9938 assert(monthsToMonth(Month.may, Month.jul) == 2);
9939 assert(monthsToMonth(Month.may, Month.aug) == 3);
9940 assert(monthsToMonth(Month.may, Month.sep) == 4);
9941 assert(monthsToMonth(Month.may, Month.oct) == 5);
9942 assert(monthsToMonth(Month.may, Month.nov) == 6);
9943 assert(monthsToMonth(Month.may, Month.dec) == 7);
9944
9945 assert(monthsToMonth(Month.oct, Month.jan) == 3);
9946 assert(monthsToMonth(Month.oct, Month.feb) == 4);
9947 assert(monthsToMonth(Month.oct, Month.mar) == 5);
9948 assert(monthsToMonth(Month.oct, Month.apr) == 6);
9949 assert(monthsToMonth(Month.oct, Month.may) == 7);
9950 assert(monthsToMonth(Month.oct, Month.jun) == 8);
9951 assert(monthsToMonth(Month.oct, Month.jul) == 9);
9952 assert(monthsToMonth(Month.oct, Month.aug) == 10);
9953 assert(monthsToMonth(Month.oct, Month.sep) == 11);
9954 assert(monthsToMonth(Month.oct, Month.oct) == 0);
9955 assert(monthsToMonth(Month.oct, Month.nov) == 1);
9956 assert(monthsToMonth(Month.oct, Month.dec) == 2);
9957
9958 assert(monthsToMonth(Month.dec, Month.jan) == 1);
9959 assert(monthsToMonth(Month.dec, Month.feb) == 2);
9960 assert(monthsToMonth(Month.dec, Month.mar) == 3);
9961 assert(monthsToMonth(Month.dec, Month.apr) == 4);
9962 assert(monthsToMonth(Month.dec, Month.may) == 5);
9963 assert(monthsToMonth(Month.dec, Month.jun) == 6);
9964 assert(monthsToMonth(Month.dec, Month.jul) == 7);
9965 assert(monthsToMonth(Month.dec, Month.aug) == 8);
9966 assert(monthsToMonth(Month.dec, Month.sep) == 9);
9967 assert(monthsToMonth(Month.dec, Month.oct) == 10);
9968 assert(monthsToMonth(Month.dec, Month.nov) == 11);
9969 assert(monthsToMonth(Month.dec, Month.dec) == 0);
9970 }
9971
9972
9973 /++
9974 Whether the given Gregorian Year is a leap year.
9975
9976 Params:
9977 year = The year to to be tested.
9978 +/
9979 bool yearIsLeapYear(int year) @safe pure nothrow @nogc
9980 {
9981 if (year % 400 == 0)
9982 return true;
9983 if (year % 100 == 0)
9984 return false;
9985 return year % 4 == 0;
9986 }
9987
9988 ///
9989 @safe unittest
9990 {
9991 foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
9992 {
9993 assert(!yearIsLeapYear(year));
9994 assert(!yearIsLeapYear(-year));
9995 }
9996
9997 foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
9998 {
9999 assert(yearIsLeapYear(year));
10000 assert(yearIsLeapYear(-year));
10001 }
10002 }
10003
10004 @safe unittest
10005 {
10006 import std.format : format;
10007 foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
10008 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
10009 {
10010 assert(!yearIsLeapYear(year), format("year: %s.", year));
10011 assert(!yearIsLeapYear(-year), format("year: %s.", year));
10012 }
10013
10014 foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
10015 {
10016 assert(yearIsLeapYear(year), format("year: %s.", year));
10017 assert(yearIsLeapYear(-year), format("year: %s.", year));
10018 }
10019 }
10020
10021
10022 /++
10023 Whether the given type defines all of the necessary functions for it to
10024 function as a time point.
10025
10026 1. `T` must define a static property named `min` which is the smallest
10027 value of `T` as `Unqual!T`.
10028
10029 2. `T` must define a static property named `max` which is the largest
10030 value of `T` as `Unqual!T`.
10031
10032 3. `T` must define an `opBinary` for addition and subtraction that
10033 accepts $(REF Duration, core,time) and returns `Unqual!T`.
10034
10035 4. `T` must define an `opOpAssign` for addition and subtraction that
10036 accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
10037
10038 5. `T` must define a `opBinary` for subtraction which accepts `T`
10039 and returns $(REF Duration, core,time).
10040 +/
10041 template isTimePoint(T)
10042 {
10043 import core.time : Duration;
10044 import std.traits : FunctionAttribute, functionAttributes, Unqual;
10045
10046 enum isTimePoint = hasMin &&
10047 hasMax &&
10048 hasOverloadedOpBinaryWithDuration &&
10049 hasOverloadedOpAssignWithDuration &&
10050 hasOverloadedOpBinaryWithSelf &&
10051 !is(U == Duration);
10052
10053 private:
10054
10055 alias U = Unqual!T;
10056
10057 enum hasMin = __traits(hasMember, T, "min") &&
10058 is(typeof(T.min) == U) &&
10059 is(typeof({static assert(__traits(isStaticFunction, T.min));}));
10060
10061 enum hasMax = __traits(hasMember, T, "max") &&
10062 is(typeof(T.max) == U) &&
10063 is(typeof({static assert(__traits(isStaticFunction, T.max));}));
10064
10065 enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
10066 is(typeof(T.init - Duration.init) == U);
10067
10068 enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
10069 is(typeof(U.init -= Duration.init) == U) &&
10070 is(typeof(
10071 {
10072 alias add = U.opOpAssign!"+";
10073 alias sub = U.opOpAssign!"-";
10074 alias FA = FunctionAttribute;
10075 static assert((functionAttributes!add & FA.ref_) != 0);
10076 static assert((functionAttributes!sub & FA.ref_) != 0);
10077 }));
10078
10079 enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
10080 }
10081
10082 ///
10083 @safe unittest
10084 {
10085 import core.time : Duration;
10086 import std.datetime.interval : Interval;
10087 import std.datetime.systime : SysTime;
10088
10089 static assert(isTimePoint!Date);
10090 static assert(isTimePoint!DateTime);
10091 static assert(isTimePoint!SysTime);
10092 static assert(isTimePoint!TimeOfDay);
10093
10094 static assert(!isTimePoint!int);
10095 static assert(!isTimePoint!Duration);
10096 static assert(!isTimePoint!(Interval!SysTime));
10097 }
10098
10099 @safe unittest
10100 {
10101 import core.time;
10102 import std.datetime.interval;
10103 import std.datetime.systime;
10104 import std.meta : AliasSeq;
10105
10106 static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
10107 {
10108 static assert(isTimePoint!(const TP), TP.stringof);
10109 static assert(isTimePoint!(immutable TP), TP.stringof);
10110 }
10111
10112 static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
10113 static assert(!isTimePoint!T, T.stringof);
10114 }
10115
10116
10117 /++
10118 Whether all of the given strings are valid units of time.
10119
10120 `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
10121 can handle precision greater than hnsecs, and the few functions in core.time
10122 which deal with "nsecs" deal with it explicitly.
10123 +/
10124 bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
10125 {
10126 import std.algorithm.searching : canFind;
10127 foreach (str; units)
10128 {
10129 if (!canFind(timeStrings[], str))
10130 return false;
10131 }
10132 return true;
10133 }
10134
10135 ///
10136 @safe @nogc nothrow unittest
10137 {
10138 assert(validTimeUnits("msecs", "seconds", "minutes"));
10139 assert(validTimeUnits("days", "weeks", "months"));
10140 assert(!validTimeUnits("ms", "seconds", "minutes"));
10141 }
10142
10143
10144 /++
10145 Compares two time unit strings. `"years"` are the largest units and
10146 `"hnsecs"` are the smallest.
10147
10148 Returns:
10149 $(BOOKTABLE,
10150 $(TR $(TD this < rhs) $(TD < 0))
10151 $(TR $(TD this == rhs) $(TD 0))
10152 $(TR $(TD this > rhs) $(TD > 0))
10153 )
10154
10155 Throws:
10156 $(LREF DateTimeException) if either of the given strings is not a valid
10157 time unit string.
10158 +/
10159 int cmpTimeUnits(string lhs, string rhs) @safe pure
10160 {
10161 import std.algorithm.searching : countUntil;
10162 import std.exception : enforce;
10163 import std.format : format;
10164
10165 immutable indexOfLHS = countUntil(timeStrings, lhs);
10166 immutable indexOfRHS = countUntil(timeStrings, rhs);
10167
10168 enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
10169 enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
10170
10171 if (indexOfLHS < indexOfRHS)
10172 return -1;
10173 if (indexOfLHS > indexOfRHS)
10174 return 1;
10175
10176 return 0;
10177 }
10178
10179 ///
10180 @safe pure unittest
10181 {
10182 import std.exception : assertThrown;
10183
10184 assert(cmpTimeUnits("hours", "hours") == 0);
10185 assert(cmpTimeUnits("hours", "weeks") < 0);
10186 assert(cmpTimeUnits("months", "seconds") > 0);
10187
10188 assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
10189 }
10190
10191 @safe unittest
10192 {
10193 foreach (i, outerUnits; timeStrings)
10194 {
10195 assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
10196
10197 // For some reason, $ won't compile.
10198 foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
10199 assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
10200 }
10201
10202 foreach (i, outerUnits; timeStrings)
10203 {
10204 foreach (innerUnits; timeStrings[0 .. i])
10205 assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
10206 }
10207 }
10208
10209
10210 /++
10211 Compares two time unit strings at compile time. `"years"` are the largest
10212 units and `"hnsecs"` are the smallest.
10213
10214 This template is used instead of `cmpTimeUnits` because exceptions
10215 can't be thrown at compile time and `cmpTimeUnits` must enforce that
10216 the strings it's given are valid time unit strings. This template uses a
10217 template constraint instead.
10218
10219 Returns:
10220 $(BOOKTABLE,
10221 $(TR $(TD this < rhs) $(TD < 0))
10222 $(TR $(TD this == rhs) $(TD 0))
10223 $(TR $(TD this > rhs) $(TD > 0))
10224 )
10225 +/
10226 template CmpTimeUnits(string lhs, string rhs)
10227 if (validTimeUnits(lhs, rhs))
10228 {
10229 enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
10230 }
10231
10232 ///
10233 @safe pure unittest
10234 {
10235 static assert(CmpTimeUnits!("years", "weeks") > 0);
10236 static assert(CmpTimeUnits!("days", "days") == 0);
10237 static assert(CmpTimeUnits!("seconds", "hours") < 0);
10238 }
10239
10240 // Helper function for CmpTimeUnits.
10241 private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
10242 {
10243 import std.algorithm.searching : countUntil;
10244 auto tstrings = timeStrings;
10245 immutable indexOfLHS = countUntil(tstrings, lhs);
10246 immutable indexOfRHS = countUntil(tstrings, rhs);
10247
10248 if (indexOfLHS < indexOfRHS)
10249 return -1;
10250 if (indexOfLHS > indexOfRHS)
10251 return 1;
10252
10253 return 0;
10254 }
10255
10256 @safe unittest
10257 {
10258 static foreach (i; 0 .. timeStrings.length)
10259 {
10260 static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
10261
10262 static foreach (next; timeStrings[i + 1 .. $])
10263 static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
10264
10265 static foreach (prev; timeStrings[0 .. i])
10266 static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
10267 }
10268 }
10269
10270
10271 package:
10272
10273
10274 /+
10275 Array of the short (three letter) names of each month.
10276 +/
10277 immutable string[12] _monthNames = ["Jan",
10278 "Feb",
10279 "Mar",
10280 "Apr",
10281 "May",
10282 "Jun",
10283 "Jul",
10284 "Aug",
10285 "Sep",
10286 "Oct",
10287 "Nov",
10288 "Dec"];
10289
10290 /+
10291 The maximum valid Day in the given month in the given year.
10292
10293 Params:
10294 year = The year to get the day for.
10295 month = The month of the Gregorian Calendar to get the day for.
10296 +/
10297 ubyte maxDay(int year, int month) @safe pure nothrow @nogc
10298 in
10299 {
10300 assert(valid!"months"(month));
10301 }
10302 do
10303 {
10304 switch (month)
10305 {
10306 case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
10307 return 31;
10308 case Month.feb:
10309 return yearIsLeapYear(year) ? 29 : 28;
10310 case Month.apr, Month.jun, Month.sep, Month.nov:
10311 return 30;
10312 default:
10313 assert(0, "Invalid month.");
10314 }
10315 }
10316
10317 @safe unittest
10318 {
10319 // Test A.D.
10320 assert(maxDay(1999, 1) == 31);
10321 assert(maxDay(1999, 2) == 28);
10322 assert(maxDay(1999, 3) == 31);
10323 assert(maxDay(1999, 4) == 30);
10324 assert(maxDay(1999, 5) == 31);
10325 assert(maxDay(1999, 6) == 30);
10326 assert(maxDay(1999, 7) == 31);
10327 assert(maxDay(1999, 8) == 31);
10328 assert(maxDay(1999, 9) == 30);
10329 assert(maxDay(1999, 10) == 31);
10330 assert(maxDay(1999, 11) == 30);
10331 assert(maxDay(1999, 12) == 31);
10332
10333 assert(maxDay(2000, 1) == 31);
10334 assert(maxDay(2000, 2) == 29);
10335 assert(maxDay(2000, 3) == 31);
10336 assert(maxDay(2000, 4) == 30);
10337 assert(maxDay(2000, 5) == 31);
10338 assert(maxDay(2000, 6) == 30);
10339 assert(maxDay(2000, 7) == 31);
10340 assert(maxDay(2000, 8) == 31);
10341 assert(maxDay(2000, 9) == 30);
10342 assert(maxDay(2000, 10) == 31);
10343 assert(maxDay(2000, 11) == 30);
10344 assert(maxDay(2000, 12) == 31);
10345
10346 // Test B.C.
10347 assert(maxDay(-1999, 1) == 31);
10348 assert(maxDay(-1999, 2) == 28);
10349 assert(maxDay(-1999, 3) == 31);
10350 assert(maxDay(-1999, 4) == 30);
10351 assert(maxDay(-1999, 5) == 31);
10352 assert(maxDay(-1999, 6) == 30);
10353 assert(maxDay(-1999, 7) == 31);
10354 assert(maxDay(-1999, 8) == 31);
10355 assert(maxDay(-1999, 9) == 30);
10356 assert(maxDay(-1999, 10) == 31);
10357 assert(maxDay(-1999, 11) == 30);
10358 assert(maxDay(-1999, 12) == 31);
10359
10360 assert(maxDay(-2000, 1) == 31);
10361 assert(maxDay(-2000, 2) == 29);
10362 assert(maxDay(-2000, 3) == 31);
10363 assert(maxDay(-2000, 4) == 30);
10364 assert(maxDay(-2000, 5) == 31);
10365 assert(maxDay(-2000, 6) == 30);
10366 assert(maxDay(-2000, 7) == 31);
10367 assert(maxDay(-2000, 8) == 31);
10368 assert(maxDay(-2000, 9) == 30);
10369 assert(maxDay(-2000, 10) == 31);
10370 assert(maxDay(-2000, 11) == 30);
10371 assert(maxDay(-2000, 12) == 31);
10372 }
10373
10374 /+
10375 Splits out a particular unit from hnsecs and gives the value for that
10376 unit and the remaining hnsecs. It really shouldn't be used unless unless
10377 all units larger than the given units have already been split out.
10378
10379 Params:
10380 units = The units to split out.
10381 hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
10382 after splitting out the given units.
10383
10384 Returns:
10385 The number of the given units from converting hnsecs to those units.
10386 +/
10387 long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
10388 if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
10389 {
10390 import core.time : convert;
10391 immutable value = convert!("hnsecs", units)(hnsecs);
10392 hnsecs -= convert!(units, "hnsecs")(value);
10393 return value;
10394 }
10395
10396 @safe unittest
10397 {
10398 auto hnsecs = 2595000000007L;
10399 immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
10400 assert(days == 3);
10401 assert(hnsecs == 3000000007);
10402
10403 immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
10404 assert(minutes == 5);
10405 assert(hnsecs == 7);
10406 }
10407
10408
10409 /+
10410 Returns the day of the week for the given day of the Gregorian Calendar.
10411
10412 Params:
10413 day = The day of the Gregorian Calendar for which to get the day of
10414 the week.
10415 +/
10416 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
10417 {
10418 // January 1st, 1 A.D. was a Monday
10419 if (day >= 0)
10420 return cast(DayOfWeek)(day % 7);
10421 else
10422 {
10423 immutable dow = cast(DayOfWeek)((day % 7) + 7);
10424
10425 if (dow == 7)
10426 return DayOfWeek.sun;
10427 else
10428 return dow;
10429 }
10430 }
10431
10432 @safe unittest
10433 {
10434 import std.datetime.systime : SysTime;
10435
10436 // Test A.D.
10437 assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
10438 assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
10439 assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
10440 assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
10441 assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
10442 assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
10443 assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
10444 assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
10445 assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
10446 assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
10447 assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
10448 assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
10449 assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10450 assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10451 assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
10452 assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
10453 assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
10454 assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
10455 assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
10456 assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
10457 assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
10458 assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
10459
10460 // Test B.C.
10461 assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
10462 assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
10463 assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
10464 assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
10465 assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
10466 assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
10467 assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
10468 assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
10469 assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
10470 }
10471
10472
10473 private:
10474
10475 enum daysInYear = 365; // The number of days in a non-leap year.
10476 enum daysInLeapYear = 366; // The numbef or days in a leap year.
10477 enum daysIn4Years = daysInYear * 3 + daysInLeapYear; // Number of days in 4 years.
10478 enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years.
10479 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
10480
10481 /+
10482 Array of integers representing the last days of each month in a year.
10483 +/
10484 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
10485
10486 /+
10487 Array of integers representing the last days of each month in a leap year.
10488 +/
10489 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
10490
10491
10492 /+
10493 Returns the string representation of the given month.
10494 +/
10495 string monthToString(Month month) @safe pure
10496 {
10497 import std.format : format;
10498 assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
10499 return _monthNames[month - Month.jan];
10500 }
10501
10502 @safe unittest
10503 {
10504 assert(monthToString(Month.jan) == "Jan");
10505 assert(monthToString(Month.feb) == "Feb");
10506 assert(monthToString(Month.mar) == "Mar");
10507 assert(monthToString(Month.apr) == "Apr");
10508 assert(monthToString(Month.may) == "May");
10509 assert(monthToString(Month.jun) == "Jun");
10510 assert(monthToString(Month.jul) == "Jul");
10511 assert(monthToString(Month.aug) == "Aug");
10512 assert(monthToString(Month.sep) == "Sep");
10513 assert(monthToString(Month.oct) == "Oct");
10514 assert(monthToString(Month.nov) == "Nov");
10515 assert(monthToString(Month.dec) == "Dec");
10516 }
10517
10518
10519 /+
10520 Returns the Month corresponding to the given string.
10521
10522 Params:
10523 monthStr = The string representation of the month to get the Month for.
10524
10525 Throws:
10526 $(REF DateTimeException,std,datetime,date) if the given month is not a
10527 valid month string.
10528 +/
10529 Month monthFromString(T)(T monthStr) @safe pure
10530 if (isSomeString!T)
10531 {
10532 import std.format : format;
10533 switch (monthStr)
10534 {
10535 case "Jan":
10536 return Month.jan;
10537 case "Feb":
10538 return Month.feb;
10539 case "Mar":
10540 return Month.mar;
10541 case "Apr":
10542 return Month.apr;
10543 case "May":
10544 return Month.may;
10545 case "Jun":
10546 return Month.jun;
10547 case "Jul":
10548 return Month.jul;
10549 case "Aug":
10550 return Month.aug;
10551 case "Sep":
10552 return Month.sep;
10553 case "Oct":
10554 return Month.oct;
10555 case "Nov":
10556 return Month.nov;
10557 case "Dec":
10558 return Month.dec;
10559 default:
10560 throw new DateTimeException(format!"Invalid month %s"(monthStr));
10561 }
10562 }
10563
10564 @safe unittest
10565 {
10566 import std.conv : to;
10567 import std.traits : EnumMembers;
10568 foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
10569 "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
10570 {
10571 assertThrown!DateTimeException(monthFromString(badStr), badStr);
10572 }
10573
10574 foreach (month; EnumMembers!Month)
10575 {
10576 assert(monthFromString(monthToString(month)) == month, month.to!string);
10577 }
10578 }
10579
10580
10581 // NOTE: all the non-simple array literals are wrapped in functions, because
10582 // otherwise importing causes re-evaluation of the static initializers using
10583 // CTFE with unittests enabled
10584 version (StdUnittest)
10585 {
10586 private @safe:
10587 // All of these helper arrays are sorted in ascending order.
10588 auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
10589 auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
10590
10591 // I'd use a Tuple, but I get forward reference errors if I try.
10592 struct MonthDay
10593 {
10594 Month month;
10595 short day;
10596
10597 this(int m, short d)
10598 {
10599 month = cast(Month) m;
10600 day = d;
10601 }
10602 }
10603
10604 MonthDay[] testMonthDays()
10605 {
10606 static MonthDay[] result = [MonthDay(1, 1),
10607 MonthDay(1, 2),
10608 MonthDay(3, 17),
10609 MonthDay(7, 4),
10610 MonthDay(10, 27),
10611 MonthDay(12, 30),
10612 MonthDay(12, 31)];
10613 return result;
10614 }
10615
10616 auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
10617
10618 TimeOfDay[] testTODs()
10619 {
10620 static result = [TimeOfDay(0, 0, 0),
10621 TimeOfDay(0, 0, 1),
10622 TimeOfDay(0, 1, 0),
10623 TimeOfDay(1, 0, 0),
10624 TimeOfDay(13, 13, 13),
10625 TimeOfDay(23, 59, 59)];
10626 return result;
10627 }
10628
10629 auto testHours = [0, 1, 12, 22, 23];
10630 auto testMinSecs = [0, 1, 30, 58, 59];
10631
10632 // Throwing exceptions is incredibly expensive, so we want to use a smaller
10633 // set of values for tests using assertThrown.
10634 TimeOfDay[] testTODsThrown()
10635 {
10636 static result = [TimeOfDay(0, 0, 0),
10637 TimeOfDay(13, 13, 13),
10638 TimeOfDay(23, 59, 59)];
10639 return result;
10640 }
10641
10642 Date[] testDatesBC;
10643 Date[] testDatesAD;
10644
10645 DateTime[] testDateTimesBC;
10646 DateTime[] testDateTimesAD;
10647
10648 // I'd use a Tuple, but I get forward reference errors if I try.
10649 struct GregDay { int day; Date date; }
10650 GregDay[] testGregDaysBC()
10651 {
10652 static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
10653 GregDay(-735_233, Date(-2012, 1, 1)),
10654 GregDay(-735_202, Date(-2012, 2, 1)),
10655 GregDay(-735_175, Date(-2012, 2, 28)),
10656 GregDay(-735_174, Date(-2012, 2, 29)),
10657 GregDay(-735_173, Date(-2012, 3, 1)),
10658 GregDay(-734_502, Date(-2010, 1, 1)),
10659 GregDay(-734_472, Date(-2010, 1, 31)),
10660 GregDay(-734_471, Date(-2010, 2, 1)),
10661 GregDay(-734_444, Date(-2010, 2, 28)),
10662 GregDay(-734_443, Date(-2010, 3, 1)),
10663 GregDay(-734_413, Date(-2010, 3, 31)),
10664 GregDay(-734_412, Date(-2010, 4, 1)),
10665 GregDay(-734_383, Date(-2010, 4, 30)),
10666 GregDay(-734_382, Date(-2010, 5, 1)),
10667 GregDay(-734_352, Date(-2010, 5, 31)),
10668 GregDay(-734_351, Date(-2010, 6, 1)),
10669 GregDay(-734_322, Date(-2010, 6, 30)),
10670 GregDay(-734_321, Date(-2010, 7, 1)),
10671 GregDay(-734_291, Date(-2010, 7, 31)),
10672 GregDay(-734_290, Date(-2010, 8, 1)),
10673 GregDay(-734_260, Date(-2010, 8, 31)),
10674 GregDay(-734_259, Date(-2010, 9, 1)),
10675 GregDay(-734_230, Date(-2010, 9, 30)),
10676 GregDay(-734_229, Date(-2010, 10, 1)),
10677 GregDay(-734_199, Date(-2010, 10, 31)),
10678 GregDay(-734_198, Date(-2010, 11, 1)),
10679 GregDay(-734_169, Date(-2010, 11, 30)),
10680 GregDay(-734_168, Date(-2010, 12, 1)),
10681 GregDay(-734_139, Date(-2010, 12, 30)),
10682 GregDay(-734_138, Date(-2010, 12, 31)),
10683 GregDay(-731_215, Date(-2001, 1, 1)),
10684 GregDay(-730_850, Date(-2000, 1, 1)),
10685 GregDay(-730_849, Date(-2000, 1, 2)),
10686 GregDay(-730_486, Date(-2000, 12, 30)),
10687 GregDay(-730_485, Date(-2000, 12, 31)),
10688 GregDay(-730_484, Date(-1999, 1, 1)),
10689 GregDay(-694_690, Date(-1901, 1, 1)),
10690 GregDay(-694_325, Date(-1900, 1, 1)),
10691 GregDay(-585_118, Date(-1601, 1, 1)),
10692 GregDay(-584_753, Date(-1600, 1, 1)),
10693 GregDay(-584_388, Date(-1600, 12, 31)),
10694 GregDay(-584_387, Date(-1599, 1, 1)),
10695 GregDay(-365_972, Date(-1001, 1, 1)),
10696 GregDay(-365_607, Date(-1000, 1, 1)),
10697 GregDay(-183_351, Date(-501, 1, 1)),
10698 GregDay(-182_986, Date(-500, 1, 1)),
10699 GregDay(-182_621, Date(-499, 1, 1)),
10700 GregDay(-146_827, Date(-401, 1, 1)),
10701 GregDay(-146_462, Date(-400, 1, 1)),
10702 GregDay(-146_097, Date(-400, 12, 31)),
10703 GregDay(-110_302, Date(-301, 1, 1)),
10704 GregDay(-109_937, Date(-300, 1, 1)),
10705 GregDay(-73_778, Date(-201, 1, 1)),
10706 GregDay(-73_413, Date(-200, 1, 1)),
10707 GregDay(-38_715, Date(-105, 1, 1)),
10708 GregDay(-37_254, Date(-101, 1, 1)),
10709 GregDay(-36_889, Date(-100, 1, 1)),
10710 GregDay(-36_524, Date(-99, 1, 1)),
10711 GregDay(-36_160, Date(-99, 12, 31)),
10712 GregDay(-35_794, Date(-97, 1, 1)),
10713 GregDay(-18_627, Date(-50, 1, 1)),
10714 GregDay(-18_262, Date(-49, 1, 1)),
10715 GregDay(-3652, Date(-9, 1, 1)),
10716 GregDay(-2191, Date(-5, 1, 1)),
10717 GregDay(-1827, Date(-5, 12, 31)),
10718 GregDay(-1826, Date(-4, 1, 1)),
10719 GregDay(-1825, Date(-4, 1, 2)),
10720 GregDay(-1462, Date(-4, 12, 30)),
10721 GregDay(-1461, Date(-4, 12, 31)),
10722 GregDay(-1460, Date(-3, 1, 1)),
10723 GregDay(-1096, Date(-3, 12, 31)),
10724 GregDay(-1095, Date(-2, 1, 1)),
10725 GregDay(-731, Date(-2, 12, 31)),
10726 GregDay(-730, Date(-1, 1, 1)),
10727 GregDay(-367, Date(-1, 12, 30)),
10728 GregDay(-366, Date(-1, 12, 31)),
10729 GregDay(-365, Date(0, 1, 1)),
10730 GregDay(-31, Date(0, 11, 30)),
10731 GregDay(-30, Date(0, 12, 1)),
10732 GregDay(-1, Date(0, 12, 30)),
10733 GregDay(0, Date(0, 12, 31))];
10734 return result;
10735 }
10736
10737 GregDay[] testGregDaysAD()
10738 {
10739 static result = [GregDay(1, Date(1, 1, 1)),
10740 GregDay(2, Date(1, 1, 2)),
10741 GregDay(32, Date(1, 2, 1)),
10742 GregDay(365, Date(1, 12, 31)),
10743 GregDay(366, Date(2, 1, 1)),
10744 GregDay(731, Date(3, 1, 1)),
10745 GregDay(1096, Date(4, 1, 1)),
10746 GregDay(1097, Date(4, 1, 2)),
10747 GregDay(1460, Date(4, 12, 30)),
10748 GregDay(1461, Date(4, 12, 31)),
10749 GregDay(1462, Date(5, 1, 1)),
10750 GregDay(17_898, Date(50, 1, 1)),
10751 GregDay(35_065, Date(97, 1, 1)),
10752 GregDay(36_160, Date(100, 1, 1)),
10753 GregDay(36_525, Date(101, 1, 1)),
10754 GregDay(37_986, Date(105, 1, 1)),
10755 GregDay(72_684, Date(200, 1, 1)),
10756 GregDay(73_049, Date(201, 1, 1)),
10757 GregDay(109_208, Date(300, 1, 1)),
10758 GregDay(109_573, Date(301, 1, 1)),
10759 GregDay(145_732, Date(400, 1, 1)),
10760 GregDay(146_098, Date(401, 1, 1)),
10761 GregDay(182_257, Date(500, 1, 1)),
10762 GregDay(182_622, Date(501, 1, 1)),
10763 GregDay(364_878, Date(1000, 1, 1)),
10764 GregDay(365_243, Date(1001, 1, 1)),
10765 GregDay(584_023, Date(1600, 1, 1)),
10766 GregDay(584_389, Date(1601, 1, 1)),
10767 GregDay(693_596, Date(1900, 1, 1)),
10768 GregDay(693_961, Date(1901, 1, 1)),
10769 GregDay(729_755, Date(1999, 1, 1)),
10770 GregDay(730_120, Date(2000, 1, 1)),
10771 GregDay(730_121, Date(2000, 1, 2)),
10772 GregDay(730_484, Date(2000, 12, 30)),
10773 GregDay(730_485, Date(2000, 12, 31)),
10774 GregDay(730_486, Date(2001, 1, 1)),
10775 GregDay(733_773, Date(2010, 1, 1)),
10776 GregDay(733_774, Date(2010, 1, 2)),
10777 GregDay(733_803, Date(2010, 1, 31)),
10778 GregDay(733_804, Date(2010, 2, 1)),
10779 GregDay(733_831, Date(2010, 2, 28)),
10780 GregDay(733_832, Date(2010, 3, 1)),
10781 GregDay(733_862, Date(2010, 3, 31)),
10782 GregDay(733_863, Date(2010, 4, 1)),
10783 GregDay(733_892, Date(2010, 4, 30)),
10784 GregDay(733_893, Date(2010, 5, 1)),
10785 GregDay(733_923, Date(2010, 5, 31)),
10786 GregDay(733_924, Date(2010, 6, 1)),
10787 GregDay(733_953, Date(2010, 6, 30)),
10788 GregDay(733_954, Date(2010, 7, 1)),
10789 GregDay(733_984, Date(2010, 7, 31)),
10790 GregDay(733_985, Date(2010, 8, 1)),
10791 GregDay(734_015, Date(2010, 8, 31)),
10792 GregDay(734_016, Date(2010, 9, 1)),
10793 GregDay(734_045, Date(2010, 9, 30)),
10794 GregDay(734_046, Date(2010, 10, 1)),
10795 GregDay(734_076, Date(2010, 10, 31)),
10796 GregDay(734_077, Date(2010, 11, 1)),
10797 GregDay(734_106, Date(2010, 11, 30)),
10798 GregDay(734_107, Date(2010, 12, 1)),
10799 GregDay(734_136, Date(2010, 12, 30)),
10800 GregDay(734_137, Date(2010, 12, 31)),
10801 GregDay(734_503, Date(2012, 1, 1)),
10802 GregDay(734_534, Date(2012, 2, 1)),
10803 GregDay(734_561, Date(2012, 2, 28)),
10804 GregDay(734_562, Date(2012, 2, 29)),
10805 GregDay(734_563, Date(2012, 3, 1)),
10806 GregDay(734_858, Date(2012, 12, 21))];
10807 return result;
10808 }
10809
10810 // I'd use a Tuple, but I get forward reference errors if I try.
10811 struct DayOfYear { int day; MonthDay md; }
10812 DayOfYear[] testDaysOfYear()
10813 {
10814 static result = [DayOfYear(1, MonthDay(1, 1)),
10815 DayOfYear(2, MonthDay(1, 2)),
10816 DayOfYear(3, MonthDay(1, 3)),
10817 DayOfYear(31, MonthDay(1, 31)),
10818 DayOfYear(32, MonthDay(2, 1)),
10819 DayOfYear(59, MonthDay(2, 28)),
10820 DayOfYear(60, MonthDay(3, 1)),
10821 DayOfYear(90, MonthDay(3, 31)),
10822 DayOfYear(91, MonthDay(4, 1)),
10823 DayOfYear(120, MonthDay(4, 30)),
10824 DayOfYear(121, MonthDay(5, 1)),
10825 DayOfYear(151, MonthDay(5, 31)),
10826 DayOfYear(152, MonthDay(6, 1)),
10827 DayOfYear(181, MonthDay(6, 30)),
10828 DayOfYear(182, MonthDay(7, 1)),
10829 DayOfYear(212, MonthDay(7, 31)),
10830 DayOfYear(213, MonthDay(8, 1)),
10831 DayOfYear(243, MonthDay(8, 31)),
10832 DayOfYear(244, MonthDay(9, 1)),
10833 DayOfYear(273, MonthDay(9, 30)),
10834 DayOfYear(274, MonthDay(10, 1)),
10835 DayOfYear(304, MonthDay(10, 31)),
10836 DayOfYear(305, MonthDay(11, 1)),
10837 DayOfYear(334, MonthDay(11, 30)),
10838 DayOfYear(335, MonthDay(12, 1)),
10839 DayOfYear(363, MonthDay(12, 29)),
10840 DayOfYear(364, MonthDay(12, 30)),
10841 DayOfYear(365, MonthDay(12, 31))];
10842 return result;
10843 }
10844
10845 DayOfYear[] testDaysOfLeapYear()
10846 {
10847 static result = [DayOfYear(1, MonthDay(1, 1)),
10848 DayOfYear(2, MonthDay(1, 2)),
10849 DayOfYear(3, MonthDay(1, 3)),
10850 DayOfYear(31, MonthDay(1, 31)),
10851 DayOfYear(32, MonthDay(2, 1)),
10852 DayOfYear(59, MonthDay(2, 28)),
10853 DayOfYear(60, MonthDay(2, 29)),
10854 DayOfYear(61, MonthDay(3, 1)),
10855 DayOfYear(91, MonthDay(3, 31)),
10856 DayOfYear(92, MonthDay(4, 1)),
10857 DayOfYear(121, MonthDay(4, 30)),
10858 DayOfYear(122, MonthDay(5, 1)),
10859 DayOfYear(152, MonthDay(5, 31)),
10860 DayOfYear(153, MonthDay(6, 1)),
10861 DayOfYear(182, MonthDay(6, 30)),
10862 DayOfYear(183, MonthDay(7, 1)),
10863 DayOfYear(213, MonthDay(7, 31)),
10864 DayOfYear(214, MonthDay(8, 1)),
10865 DayOfYear(244, MonthDay(8, 31)),
10866 DayOfYear(245, MonthDay(9, 1)),
10867 DayOfYear(274, MonthDay(9, 30)),
10868 DayOfYear(275, MonthDay(10, 1)),
10869 DayOfYear(305, MonthDay(10, 31)),
10870 DayOfYear(306, MonthDay(11, 1)),
10871 DayOfYear(335, MonthDay(11, 30)),
10872 DayOfYear(336, MonthDay(12, 1)),
10873 DayOfYear(364, MonthDay(12, 29)),
10874 DayOfYear(365, MonthDay(12, 30)),
10875 DayOfYear(366, MonthDay(12, 31))];
10876 return result;
10877 }
10878
10879 void initializeTests()
10880 {
10881 foreach (year; testYearsBC)
10882 {
10883 foreach (md; testMonthDays)
10884 testDatesBC ~= Date(year, md.month, md.day);
10885 }
10886
10887 foreach (year; testYearsAD)
10888 {
10889 foreach (md; testMonthDays)
10890 testDatesAD ~= Date(year, md.month, md.day);
10891 }
10892
10893 foreach (dt; testDatesBC)
10894 {
10895 foreach (tod; testTODs)
10896 testDateTimesBC ~= DateTime(dt, tod);
10897 }
10898
10899 foreach (dt; testDatesAD)
10900 {
10901 foreach (tod; testTODs)
10902 testDateTimesAD ~= DateTime(dt, tod);
10903 }
10904 }
10905 }