]> git.ipfire.org Git - thirdparty/gcc.git/blame - libphobos/src/std/datetime/date.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / libphobos / src / std / datetime / date.d
CommitLineData
b4c522fa 1// Written in the D programming language
b4c522fa 2/++
5fee5ec3
IB
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
b4c522fa 38 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
5fee5ec3
IB
39 Authors: $(HTTP jmdavisprog.com, Jonathan M Davis)
40 Source: $(PHOBOSSRC std/datetime/date.d)
b4c522fa
IB
41+/
42module std.datetime.date;
43
5fee5ec3 44import core.time : TimeException;
b4c522fa
IB
45import std.traits : isSomeString, Unqual;
46import std.typecons : Flag;
5fee5ec3 47import std.range.primitives : isOutputRange;
b4c522fa 48
5fee5ec3 49version (StdUnittest) import std.exception : assertThrown;
b4c522fa
IB
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 +/
62alias DateTimeException = TimeException;
63
64
65/++
66 Represents the 12 months of the Gregorian year (January is 1).
67 +/
68enum 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
5fee5ec3
IB
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
b4c522fa
IB
91
92/++
93 Represents the 7 days of the Gregorian week (Sunday is 0).
94 +/
95enum DayOfWeek : ubyte
96{
97 sun = 0, ///
98 mon, ///
99 tue, ///
100 wed, ///
101 thu, ///
102 fri, ///
103 sat ///
104}
105
5fee5ec3
IB
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}
b4c522fa
IB
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
5fee5ec3 125 If set to `AllowDayOverflow.no`, then day overflow is not allowed.
b4c522fa 126
5fee5ec3 127 Otherwise, if set to `AllowDayOverflow.yes`, then day overflow is
b4c522fa
IB
128 allowed.
129 +/
130alias AllowDayOverflow = Flag!"allowDayOverflow";
131
132
133/++
134 Array of the strings representing time units, starting with the smallest
5fee5ec3 135 unit and going to the largest. It does not include `"nsecs"`.
b4c522fa 136
5fee5ec3
IB
137 Includes `"hnsecs"` (hecto-nanoseconds (100 ns)),
138 `"usecs"` (microseconds), `"msecs"` (milliseconds), `"seconds"`,
139 `"minutes"`, `"hours"`, `"days"`, `"weeks"`, `"months"`, and
140 `"years"`
b4c522fa
IB
141 +/
142immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes",
143 "hours", "days", "weeks", "months", "years"];
144
145
146/++
5fee5ec3
IB
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.
b4c522fa
IB
156 +/
157struct DateTime
158{
159public:
160
161 /++
162 Params:
163 date = The date portion of $(LREF DateTime).
164 tod = The time portion of $(LREF DateTime).
165 +/
5fee5ec3 166 this(Date date, TimeOfDay tod = TimeOfDay.init) @safe pure nothrow @nogc
b4c522fa
IB
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 /++
5fee5ec3 226 Compares this $(LREF DateTime) with the given `DateTime.`.
b4c522fa
IB
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 +/
5fee5ec3 235 int opCmp(DateTime rhs) const @safe pure nothrow @nogc
b4c522fa
IB
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 +/
5fee5ec3 475 @property void date(Date date) @safe pure nothrow @nogc
b4c522fa
IB
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 +/
5fee5ec3 528 @property void timeOfDay(TimeOfDay tod) @safe pure nothrow @nogc
b4c522fa
IB
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 {
5fee5ec3 595 static void testDT(DateTime dt, int year, DateTime expected, size_t line = __LINE__)
b4c522fa
IB
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:
5fee5ec3 622 $(REF DateTimeException,std,datetime,date) if `isAD` is true.
b4c522fa
IB
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 {
5fee5ec3 639 assertThrown!DateTimeException((DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1))));
b4c522fa
IB
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 {
5fee5ec3 737 static void testDT(DateTime dt, Month month, DateTime expected = DateTime.init, size_t line = __LINE__)
b4c522fa
IB
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 /++
5fee5ec3
IB
1055 Adds the given number of years or months to this $(LREF DateTime),
1056 mutating it. A negative number will subtract.
b4c522fa
IB
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.
5fee5ec3
IB
1072
1073 Returns:
1074 A reference to the `DateTime` (`this`).
b4c522fa
IB
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 /++
5fee5ec3
IB
1120 Adds the given number of years or months to this $(LREF DateTime),
1121 mutating it. A negative number will subtract.
b4c522fa
IB
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.
5fee5ec3
IB
1137
1138 Returns:
1139 A reference to the `DateTime` (`this`).
b4c522fa
IB
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 /++
5fee5ec3
IB
1193 Adds the given number of units to this $(LREF DateTime), mutating it. A
1194 negative number will subtract.
b4c522fa
IB
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
5fee5ec3
IB
1200 Accepted units are `"days"`, `"minutes"`, `"hours"`,
1201 `"minutes"`, and `"seconds"`.
b4c522fa
IB
1202
1203 Params:
1204 units = The units to add.
1205 value = The number of $(D_PARAM units) to add to this
1206 $(LREF DateTime).
5fee5ec3
IB
1207
1208 Returns:
1209 A reference to the `DateTime` (`this`).
b4c522fa
IB
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
5fee5ec3 1251 /// ditto
b4c522fa
IB
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 {
5fee5ec3 1264 static void testDT(DateTime orig, int hours, DateTime expected, size_t line = __LINE__)
b4c522fa
IB
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 {
5fee5ec3 1567 static void testDT(DateTime orig, int minutes, DateTime expected, size_t line = __LINE__)
b4c522fa
IB
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 {
5fee5ec3 1867 static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
b4c522fa
IB
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
5fee5ec3 2123 import core.time : Duration;
b4c522fa
IB
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 {
5fee5ec3
IB
2168 import core.time : dur;
2169
b4c522fa
IB
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
b4c522fa
IB
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 +/
9fa27ed0
IB
2235 ref DateTime opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
2236 if (op == "+" || op == "-")
b4c522fa 2237 {
9fa27ed0 2238 import core.time : convert;
b4c522fa
IB
2239 import std.format : format;
2240
2241 DateTime retval = this;
9fa27ed0 2242 immutable hnsecs = duration.total!"hnsecs";
b4c522fa
IB
2243
2244 mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op));
2245 }
2246
2247 @safe unittest
2248 {
5fee5ec3 2249 import core.time : dur;
b4c522fa
IB
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
b4c522fa
IB
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 +/
5fee5ec3 2341 Duration opBinary(string op)(DateTime rhs) const @safe pure nothrow @nogc
b4c522fa
IB
2342 if (op == "-")
2343 {
2344 immutable dateResult = _date - rhs.date;
2345 immutable todResult = _tod - rhs._tod;
2346
5fee5ec3 2347 import core.time : dur;
b4c522fa
IB
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
5fee5ec3 2355 import core.time : dur;
b4c522fa
IB
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 +/
5fee5ec3 2428 int diffMonths(DateTime rhs) const @safe pure nothrow @nogc
b4c522fa
IB
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
5fee5ec3
IB
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
b4c522fa
IB
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 /++
5fee5ec3
IB
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.
b4c522fa
IB
2876 +/
2877 string toISOString() const @safe pure nothrow
2878 {
5fee5ec3
IB
2879 import std.array : appender;
2880 auto w = appender!string();
2881 w.reserve(18);
b4c522fa 2882 try
5fee5ec3 2883 toISOString(w);
b4c522fa 2884 catch (Exception e)
5fee5ec3
IB
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 );
b4c522fa
IB
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
5fee5ec3
IB
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.
b4c522fa
IB
2953 +/
2954 string toISOExtString() const @safe pure nothrow
2955 {
5fee5ec3
IB
2956 import std.array : appender;
2957 auto w = appender!string();
2958 w.reserve(20);
b4c522fa 2959 try
5fee5ec3 2960 toISOExtString(w);
b4c522fa 2961 catch (Exception e)
5fee5ec3
IB
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 );
b4c522fa
IB
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
5fee5ec3
IB
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.
b4c522fa
IB
3029 +/
3030 string toSimpleString() const @safe pure nothrow
3031 {
5fee5ec3
IB
3032 import std.array : appender;
3033 auto w = appender!string();
3034 w.reserve(22);
b4c522fa 3035 try
5fee5ec3 3036 toSimpleString(w);
b4c522fa 3037 catch (Exception e)
5fee5ec3
IB
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 );
b4c522fa
IB
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
5fee5ec3
IB
3134 /// ditto
3135 void toString(W)(ref W writer) const
3136 if (isOutputRange!(W, char))
3137 {
3138 toSimpleString(writer);
3139 }
b4c522fa
IB
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 +/
5fee5ec3 3153 static DateTime fromISOString(S)(scope const S isoString) @safe pure
b4c522fa
IB
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;
5fee5ec3 3160 import std.utf : byCodeUnit;
b4c522fa
IB
3161
3162 auto str = strip(isoString);
3163
3164 enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
5fee5ec3 3165 auto t = str.byCodeUnit.countUntil('T');
b4c522fa
IB
3166
3167 enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
3168
3169 immutable date = Date.fromISOString(str[0 .. t]);
3170 immutable tod = TimeOfDay.fromISOString(str[t+1 .. $]);
3171
3172 return DateTime(date, tod);
3173 }
3174
3175 ///
3176 @safe unittest
3177 {
3178 assert(DateTime.fromISOString("20100704T070612") ==
3179 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3180
3181 assert(DateTime.fromISOString("19981225T021500") ==
3182 DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3183
3184 assert(DateTime.fromISOString("00000105T230959") ==
3185 DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3186
3187 assert(DateTime.fromISOString("-00040105T000002") ==
3188 DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3189
3190 assert(DateTime.fromISOString(" 20100704T070612 ") ==
3191 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3192 }
3193
3194 @safe unittest
3195 {
3196 assertThrown!DateTimeException(DateTime.fromISOString(""));
3197 assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3198 assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3199 assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3200 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3201 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3202
3203 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3204 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3205 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3206 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3207 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3208
3209 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3210 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3211 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3212 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3213 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3214 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3215
3216 assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201"));
3217 assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01"));
3218
5fee5ec3 3219 assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
b4c522fa
IB
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
5fee5ec3 3228 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
3229 @safe unittest
3230 {
3231 import std.conv : to;
3232 import std.meta : AliasSeq;
5fee5ec3 3233 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 3234 {
5fee5ec3 3235 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
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 +/
5fee5ec3 3254 static DateTime fromISOExtString(S)(scope const S isoExtString) @safe pure
b4c522fa
IB
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;
5fee5ec3 3261 import std.utf : byCodeUnit;
b4c522fa
IB
3262
3263 auto str = strip(isoExtString);
3264
3265 enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
5fee5ec3 3266 auto t = str.byCodeUnit.countUntil('T');
b4c522fa
IB
3267
3268 enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
3269
3270 immutable date = Date.fromISOExtString(str[0 .. t]);
3271 immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3272
3273 return DateTime(date, tod);
3274 }
3275
3276 ///
3277 @safe unittest
3278 {
3279 assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
3280 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3281
3282 assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
3283 DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3284
3285 assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
3286 DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3287
3288 assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
3289 DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3290
3291 assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
3292 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3293 }
3294
3295 @safe unittest
3296 {
3297 assertThrown!DateTimeException(DateTime.fromISOExtString(""));
3298 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000"));
3299 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000"));
3300 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000"));
3301 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000."));
3302 assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0"));
3303
3304 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00"));
3305 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3306 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00"));
3307 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00"));
3308 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00."));
3309 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0"));
3310
3311 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00"));
3312 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00"));
3313 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00."));
3314 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0"));
3315
3316 assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201"));
3317 assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01"));
3318
5fee5ec3 3319 assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
b4c522fa
IB
3320 assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3321 assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3322 assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3323 assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3324 assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3325 assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3326 }
3327
5fee5ec3 3328 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
3329 @safe unittest
3330 {
3331 import std.conv : to;
3332 import std.meta : AliasSeq;
5fee5ec3 3333 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 3334 {
5fee5ec3 3335 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
3336 assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3337 }
3338 }
3339
3340
3341 /++
3342 Creates a $(LREF DateTime) from a string with the format
3343 YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string.
3344
3345 Params:
3346 simpleString = A string formatted in the way that toSimpleString
3347 formats dates and times.
3348
3349 Throws:
3350 $(REF DateTimeException,std,datetime,date) if the given string is
3351 not in the correct format or if the resulting $(LREF DateTime)
3352 would not be valid.
3353 +/
5fee5ec3 3354 static DateTime fromSimpleString(S)(scope const S simpleString) @safe pure
b4c522fa
IB
3355 if (isSomeString!(S))
3356 {
3357 import std.algorithm.searching : countUntil;
3358 import std.exception : enforce;
3359 import std.format : format;
3360 import std.string : strip;
5fee5ec3 3361 import std.utf : byCodeUnit;
b4c522fa
IB
3362
3363 auto str = strip(simpleString);
3364
3365 enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
5fee5ec3 3366 auto t = str.byCodeUnit.countUntil(' ');
b4c522fa
IB
3367
3368 enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
3369
3370 immutable date = Date.fromSimpleString(str[0 .. t]);
3371 immutable tod = TimeOfDay.fromISOExtString(str[t+1 .. $]);
3372
3373 return DateTime(date, tod);
3374 }
3375
3376 ///
3377 @safe unittest
3378 {
3379 assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
3380 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3381 assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
3382 DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
3383 assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
3384 DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
3385 assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
3386 DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
3387 assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
3388 DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
3389 }
3390
3391 @safe unittest
3392 {
3393 assertThrown!DateTimeException(DateTime.fromISOString(""));
3394 assertThrown!DateTimeException(DateTime.fromISOString("20100704000000"));
3395 assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000"));
3396 assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000"));
3397 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000."));
3398 assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0"));
3399
3400 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00"));
3401 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00"));
3402 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00"));
3403 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00."));
3404 assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0"));
3405
3406 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00"));
3407 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00"));
3408 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00"));
3409 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00"));
3410 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00."));
3411 assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0"));
3412
3413 assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201"));
3414 assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201"));
3415
3416 assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") ==
5fee5ec3 3417 DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 1)));
b4c522fa
IB
3418 assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") ==
3419 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3420 assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") ==
3421 DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)));
3422 assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") ==
3423 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3424 assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") ==
3425 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3426 assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") ==
3427 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3428 assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") ==
3429 DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)));
3430 }
3431
5fee5ec3 3432 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
3433 @safe unittest
3434 {
3435 import std.conv : to;
3436 import std.meta : AliasSeq;
5fee5ec3 3437 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 3438 {
5fee5ec3 3439 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
3440 assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
3441 }
3442 }
3443
3444
3445 /++
3446 Returns the $(LREF DateTime) farthest in the past which is representable
3447 by $(LREF DateTime).
3448 +/
3449 @property static DateTime min() @safe pure nothrow @nogc
3450 out(result)
3451 {
3452 assert(result._date == Date.min);
3453 assert(result._tod == TimeOfDay.min);
3454 }
5fee5ec3 3455 do
b4c522fa
IB
3456 {
3457 auto dt = DateTime.init;
3458 dt._date._year = short.min;
3459 dt._date._month = Month.jan;
3460 dt._date._day = 1;
3461
3462 return dt;
3463 }
3464
3465 @safe unittest
3466 {
3467 assert(DateTime.min.year < 0);
3468 assert(DateTime.min < DateTime.max);
3469 }
3470
3471
3472 /++
3473 Returns the $(LREF DateTime) farthest in the future which is
3474 representable by $(LREF DateTime).
3475 +/
3476 @property static DateTime max() @safe pure nothrow @nogc
3477 out(result)
3478 {
3479 assert(result._date == Date.max);
3480 assert(result._tod == TimeOfDay.max);
3481 }
5fee5ec3 3482 do
b4c522fa
IB
3483 {
3484 auto dt = DateTime.init;
3485 dt._date._year = short.max;
3486 dt._date._month = Month.dec;
3487 dt._date._day = 31;
3488 dt._tod._hour = TimeOfDay.maxHour;
3489 dt._tod._minute = TimeOfDay.maxMinute;
3490 dt._tod._second = TimeOfDay.maxSecond;
3491
3492 return dt;
3493 }
3494
3495 @safe unittest
3496 {
3497 assert(DateTime.max.year > 0);
3498 assert(DateTime.max > DateTime.min);
3499 }
3500
3501
3502private:
3503
3504 /+
3505 Add seconds to the time of day. Negative values will subtract. If the
3506 number of seconds overflows (or underflows), then the seconds will wrap,
3507 increasing (or decreasing) the number of minutes accordingly. The
3508 same goes for any larger units.
3509
3510 Params:
3511 seconds = The number of seconds to add to this $(LREF DateTime).
3512 +/
3513 ref DateTime _addSeconds(long seconds) return @safe pure nothrow @nogc
3514 {
5fee5ec3 3515 import core.time : convert;
b4c522fa
IB
3516 long hnsecs = convert!("seconds", "hnsecs")(seconds);
3517 hnsecs += convert!("hours", "hnsecs")(_tod._hour);
3518 hnsecs += convert!("minutes", "hnsecs")(_tod._minute);
3519 hnsecs += convert!("seconds", "hnsecs")(_tod._second);
3520
3521 auto days = splitUnitsFromHNSecs!"days"(hnsecs);
3522
3523 if (hnsecs < 0)
3524 {
3525 hnsecs += convert!("days", "hnsecs")(1);
3526 --days;
3527 }
3528
3529 _date._addDays(days);
3530
3531 immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
3532 immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
3533 immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
3534
3535 _tod._hour = cast(ubyte) newHours;
3536 _tod._minute = cast(ubyte) newMinutes;
3537 _tod._second = cast(ubyte) newSeconds;
3538
3539 return this;
3540 }
3541
3542 @safe unittest
3543 {
5fee5ec3 3544 static void testDT(DateTime orig, int seconds, DateTime expected, size_t line = __LINE__)
b4c522fa
IB
3545 {
3546 orig._addSeconds(seconds);
3547 assert(orig == expected);
3548 }
3549
3550 // Test A.D.
3551 testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33));
3552 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34));
3553 testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35));
3554 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36));
3555 testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37));
3556 testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38));
3557 testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43));
3558 testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48));
3559 testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59));
3560 testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0));
3561 testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3));
3562 testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32));
3563 testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33));
3564 testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34));
3565
3566 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59));
3567 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0));
3568 testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1));
3569 testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0));
3570 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32));
3571 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33));
3572 testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34));
3573 testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33));
3574 testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3));
3575
3576 testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32));
3577 testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31));
3578 testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30));
3579 testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29));
3580 testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28));
3581 testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23));
3582 testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18));
3583 testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0));
3584 testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59));
3585 testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58));
3586 testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34));
3587 testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33));
3588 testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32));
3589
3590 testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0));
3591 testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59));
3592 testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33));
3593 testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32));
3594 testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59));
3595 testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57));
3596
3597 testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1));
3598 testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0));
3599 testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59));
3600
3601 testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1));
3602 testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0));
3603 testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59));
3604
3605 testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1));
3606 testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0));
3607 testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59));
3608
3609 testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0));
3610 testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59));
3611 testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58));
3612
3613 testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0));
3614 testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59));
3615 testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58));
3616
3617 testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1));
3618 testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0));
3619 testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59));
3620
3621 // Test B.C.
3622 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33));
3623 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34));
3624 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35));
3625 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36));
3626 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37));
3627 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38));
3628 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43));
3629 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48));
3630 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59));
3631 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0));
3632 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3));
3633 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32));
3634 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33));
3635 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34));
3636
3637 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59));
3638 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0));
3639 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1));
3640 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0));
3641 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32));
3642 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33));
3643 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34));
3644 testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33));
3645 testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3));
3646
3647 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32));
3648 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31));
3649 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30));
3650 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29));
3651 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28));
3652 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23));
3653 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18));
3654 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0));
3655 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59));
3656 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58));
3657 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34));
3658 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33));
3659 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32));
3660
3661 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0));
3662 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59));
3663 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33));
3664 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32));
3665 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59));
3666 testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33));
3667 testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57));
3668
3669 testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1));
3670 testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0));
3671 testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59));
3672
3673 testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1));
3674 testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0));
3675 testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59));
3676
3677 testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1));
3678 testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0));
3679 testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59));
3680
3681 testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0));
3682 testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59));
3683 testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58));
3684
3685 testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0));
3686 testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59));
3687 testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58));
3688
3689 testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1));
3690 testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0));
3691 testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59));
3692
3693 // Test Both
3694 testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59));
3695 testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0));
3696
3697 testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59));
3698 testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0));
3699
3700 testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33));
3701 testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33));
3702
3703 testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50));
3704 testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33));
3705
3706 const cdt = DateTime(1999, 7, 6, 12, 30, 33);
3707 immutable idt = DateTime(1999, 7, 6, 12, 30, 33);
3708 static assert(!__traits(compiles, cdt._addSeconds(4)));
3709 static assert(!__traits(compiles, idt._addSeconds(4)));
3710 }
3711
3712
3713 Date _date;
3714 TimeOfDay _tod;
3715}
3716
5fee5ec3
IB
3717///
3718@safe pure unittest
3719{
3720 import core.time : days, seconds;
3721
3722 auto dt = DateTime(2000, 6, 1, 10, 30, 0);
3723
3724 assert(dt.date == Date(2000, 6, 1));
3725 assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
3726 assert(dt.dayOfYear == 153);
3727 assert(dt.dayOfWeek == DayOfWeek.thu);
3728
3729 dt += 10.days + 100.seconds;
3730 assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
3731
3732 assert(dt.toISOExtString() == "2000-06-11T10:31:40");
3733 assert(dt.toISOString() == "20000611T103140");
3734 assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
3735
3736 assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3737 assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
3738 assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
3739}
b4c522fa
IB
3740
3741/++
3742 Represents a date in the
3743 $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic
3744 Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years
3745 are A.D. Non-positive years are B.C.
3746
5fee5ec3 3747 Year, month, and day are kept separately internally so that `Date` is
b4c522fa
IB
3748 optimized for calendar-based operations.
3749
5fee5ec3 3750 `Date` uses the Proleptic Gregorian Calendar, so it assumes the Gregorian
b4c522fa
IB
3751 leap year calculations for its entire length. As per
3752 $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as
3753 year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C.
3754 as a positive integer with 1 B.C. being the year prior to 1 A.D.
3755
3756 Year 0 is a leap year.
3757 +/
3758struct Date
3759{
3760public:
3761
3762 /++
3763 Throws:
3764 $(REF DateTimeException,std,datetime,date) if the resulting
3765 $(LREF Date) would not be valid.
3766
3767 Params:
3768 year = Year of the Gregorian Calendar. Positive values are A.D.
3769 Non-positive values are B.C. with year 0 being the year
3770 prior to 1 A.D.
3771 month = Month of the year (January is 1).
3772 day = Day of the month.
3773 +/
3774 this(int year, int month, int day) @safe pure
3775 {
3776 enforceValid!"months"(cast(Month) month);
3777 enforceValid!"days"(year, cast(Month) month, day);
3778
3779 _year = cast(short) year;
3780 _month = cast(Month) month;
3781 _day = cast(ubyte) day;
3782 }
3783
3784 @safe unittest
3785 {
3786 import std.exception : assertNotThrown;
3787 assert(Date(1, 1, 1) == Date.init);
3788
5fee5ec3 3789 static void testDate(Date date, int year, int month, int day)
b4c522fa
IB
3790 {
3791 assert(date._year == year);
3792 assert(date._month == month);
3793 assert(date._day == day);
3794 }
3795
3796 testDate(Date(1999, 1 , 1), 1999, Month.jan, 1);
3797 testDate(Date(1999, 7 , 1), 1999, Month.jul, 1);
3798 testDate(Date(1999, 7 , 6), 1999, Month.jul, 6);
3799
3800 // Test A.D.
3801 assertThrown!DateTimeException(Date(1, 0, 1));
3802 assertThrown!DateTimeException(Date(1, 1, 0));
3803 assertThrown!DateTimeException(Date(1999, 13, 1));
3804 assertThrown!DateTimeException(Date(1999, 1, 32));
3805 assertThrown!DateTimeException(Date(1999, 2, 29));
3806 assertThrown!DateTimeException(Date(2000, 2, 30));
3807 assertThrown!DateTimeException(Date(1999, 3, 32));
3808 assertThrown!DateTimeException(Date(1999, 4, 31));
3809 assertThrown!DateTimeException(Date(1999, 5, 32));
3810 assertThrown!DateTimeException(Date(1999, 6, 31));
3811 assertThrown!DateTimeException(Date(1999, 7, 32));
3812 assertThrown!DateTimeException(Date(1999, 8, 32));
3813 assertThrown!DateTimeException(Date(1999, 9, 31));
3814 assertThrown!DateTimeException(Date(1999, 10, 32));
3815 assertThrown!DateTimeException(Date(1999, 11, 31));
3816 assertThrown!DateTimeException(Date(1999, 12, 32));
3817
3818 assertNotThrown!DateTimeException(Date(1999, 1, 31));
3819 assertNotThrown!DateTimeException(Date(1999, 2, 28));
3820 assertNotThrown!DateTimeException(Date(2000, 2, 29));
3821 assertNotThrown!DateTimeException(Date(1999, 3, 31));
3822 assertNotThrown!DateTimeException(Date(1999, 4, 30));
3823 assertNotThrown!DateTimeException(Date(1999, 5, 31));
3824 assertNotThrown!DateTimeException(Date(1999, 6, 30));
3825 assertNotThrown!DateTimeException(Date(1999, 7, 31));
3826 assertNotThrown!DateTimeException(Date(1999, 8, 31));
3827 assertNotThrown!DateTimeException(Date(1999, 9, 30));
3828 assertNotThrown!DateTimeException(Date(1999, 10, 31));
3829 assertNotThrown!DateTimeException(Date(1999, 11, 30));
3830 assertNotThrown!DateTimeException(Date(1999, 12, 31));
3831
3832 // Test B.C.
3833 assertNotThrown!DateTimeException(Date(0, 1, 1));
3834 assertNotThrown!DateTimeException(Date(-1, 1, 1));
3835 assertNotThrown!DateTimeException(Date(-1, 12, 31));
3836 assertNotThrown!DateTimeException(Date(-1, 2, 28));
3837 assertNotThrown!DateTimeException(Date(-4, 2, 29));
3838
3839 assertThrown!DateTimeException(Date(-1, 2, 29));
3840 assertThrown!DateTimeException(Date(-2, 2, 29));
3841 assertThrown!DateTimeException(Date(-3, 2, 29));
3842 }
3843
3844
3845 /++
3846 Params:
3847 day = The Xth day of the Gregorian Calendar that the constructed
3848 $(LREF Date) will be for.
3849 +/
3850 this(int day) @safe pure nothrow @nogc
3851 {
3852 if (day > 0)
3853 {
3854 int years = (day / daysIn400Years) * 400 + 1;
3855 day %= daysIn400Years;
3856
3857 {
3858 immutable tempYears = day / daysIn100Years;
3859
3860 if (tempYears == 4)
3861 {
3862 years += 300;
3863 day -= daysIn100Years * 3;
3864 }
3865 else
3866 {
3867 years += tempYears * 100;
3868 day %= daysIn100Years;
3869 }
3870 }
3871
3872 years += (day / daysIn4Years) * 4;
3873 day %= daysIn4Years;
3874
3875 {
3876 immutable tempYears = day / daysInYear;
3877
3878 if (tempYears == 4)
3879 {
3880 years += 3;
3881 day -= daysInYear * 3;
3882 }
3883 else
3884 {
3885 years += tempYears;
3886 day %= daysInYear;
3887 }
3888 }
3889
3890 if (day == 0)
3891 {
3892 _year = cast(short)(years - 1);
3893 _month = Month.dec;
3894 _day = 31;
3895 }
3896 else
3897 {
3898 _year = cast(short) years;
3899
3900 setDayOfYear(day);
3901 }
3902 }
3903 else if (day <= 0 && -day < daysInLeapYear)
3904 {
3905 _year = 0;
3906
3907 setDayOfYear(daysInLeapYear + day);
3908 }
3909 else
3910 {
3911 day += daysInLeapYear - 1;
3912 int years = (day / daysIn400Years) * 400 - 1;
3913 day %= daysIn400Years;
3914
3915 {
3916 immutable tempYears = day / daysIn100Years;
3917
3918 if (tempYears == -4)
3919 {
3920 years -= 300;
3921 day += daysIn100Years * 3;
3922 }
3923 else
3924 {
3925 years += tempYears * 100;
3926 day %= daysIn100Years;
3927 }
3928 }
3929
3930 years += (day / daysIn4Years) * 4;
3931 day %= daysIn4Years;
3932
3933 {
3934 immutable tempYears = day / daysInYear;
3935
3936 if (tempYears == -4)
3937 {
3938 years -= 3;
3939 day += daysInYear * 3;
3940 }
3941 else
3942 {
3943 years += tempYears;
3944 day %= daysInYear;
3945 }
3946 }
3947
3948 if (day == 0)
3949 {
3950 _year = cast(short)(years + 1);
3951 _month = Month.jan;
3952 _day = 1;
3953 }
3954 else
3955 {
3956 _year = cast(short) years;
3957 immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1;
3958
3959 setDayOfYear(newDoY);
3960 }
3961 }
3962 }
3963
3964 @safe unittest
3965 {
3966 import std.range : chain;
3967
3968 // Test A.D.
3969 foreach (gd; chain(testGregDaysBC, testGregDaysAD))
3970 assert(Date(gd.day) == gd.date);
3971 }
3972
3973
3974 /++
3975 Compares this $(LREF Date) with the given $(LREF Date).
3976
3977 Returns:
3978 $(BOOKTABLE,
3979 $(TR $(TD this &lt; rhs) $(TD &lt; 0))
3980 $(TR $(TD this == rhs) $(TD 0))
3981 $(TR $(TD this &gt; rhs) $(TD &gt; 0))
3982 )
3983 +/
5fee5ec3 3984 int opCmp(Date rhs) const @safe pure nothrow @nogc
b4c522fa
IB
3985 {
3986 if (_year < rhs._year)
3987 return -1;
3988 if (_year > rhs._year)
3989 return 1;
3990
3991 if (_month < rhs._month)
3992 return -1;
3993 if (_month > rhs._month)
3994 return 1;
3995
3996 if (_day < rhs._day)
3997 return -1;
3998 if (_day > rhs._day)
3999 return 1;
4000
4001 return 0;
4002 }
4003
4004 @safe unittest
4005 {
4006 // Test A.D.
4007 assert(Date(1, 1, 1).opCmp(Date.init) == 0);
4008
4009 assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0);
4010 assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0);
4011 assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0);
4012
4013 assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0);
4014 assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0);
4015
4016 assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0);
4017
4018 assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0);
4019 assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0);
4020 assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0);
4021 assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0);
4022 assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0);
4023 assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0);
4024
4025 assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0);
4026 assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4027 assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0);
4028 assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0);
4029 assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0);
4030 assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0);
4031
4032 // Test B.C.
4033 assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0);
4034 assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0);
4035 assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0);
4036 assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0);
4037
4038 assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0);
4039 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0);
4040
4041 assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0);
4042
4043 assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0);
4044 assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0);
4045 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0);
4046 assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0);
4047 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4048 assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0);
4049
4050 assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0);
4051 assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0);
4052 assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0);
4053 assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0);
4054 assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0);
4055 assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0);
4056
4057 // Test Both
4058 assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0);
4059 assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0);
4060
4061 assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0);
4062 assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0);
4063
4064 assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0);
4065 assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0);
4066
4067 assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0);
4068 assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0);
4069
4070 assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0);
4071 assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0);
4072
4073 auto date = Date(1999, 7, 6);
4074 const cdate = Date(1999, 7, 6);
4075 immutable idate = Date(1999, 7, 6);
4076 assert(date.opCmp(date) == 0);
4077 assert(date.opCmp(cdate) == 0);
4078 assert(date.opCmp(idate) == 0);
4079 assert(cdate.opCmp(date) == 0);
4080 assert(cdate.opCmp(cdate) == 0);
4081 assert(cdate.opCmp(idate) == 0);
4082 assert(idate.opCmp(date) == 0);
4083 assert(idate.opCmp(cdate) == 0);
4084 assert(idate.opCmp(idate) == 0);
4085 }
4086
4087
4088 /++
4089 Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4090 are B.C.
4091 +/
4092 @property short year() const @safe pure nothrow @nogc
4093 {
4094 return _year;
4095 }
4096
4097 ///
4098 @safe unittest
4099 {
4100 assert(Date(1999, 7, 6).year == 1999);
4101 assert(Date(2010, 10, 4).year == 2010);
4102 assert(Date(-7, 4, 5).year == -7);
4103 }
4104
4105 @safe unittest
4106 {
4107 assert(Date.init.year == 1);
4108 assert(Date(1999, 7, 6).year == 1999);
4109 assert(Date(-1999, 7, 6).year == -1999);
4110
4111 const cdate = Date(1999, 7, 6);
4112 immutable idate = Date(1999, 7, 6);
4113 assert(cdate.year == 1999);
4114 assert(idate.year == 1999);
4115 }
4116
4117 /++
4118 Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive
4119 are B.C.
4120
4121 Params:
4122 year = The year to set this Date's year to.
4123
4124 Throws:
4125 $(REF DateTimeException,std,datetime,date) if the new year is not
4126 a leap year and the resulting date would be on February 29th.
4127 +/
4128 @property void year(int year) @safe pure
4129 {
4130 enforceValid!"days"(year, _month, _day);
4131 _year = cast(short) year;
4132 }
4133
4134 ///
4135 @safe unittest
4136 {
4137 assert(Date(1999, 7, 6).year == 1999);
4138 assert(Date(2010, 10, 4).year == 2010);
4139 assert(Date(-7, 4, 5).year == -7);
4140 }
4141
4142 @safe unittest
4143 {
4144 static void testDateInvalid(Date date, int year)
4145 {
4146 date.year = year;
4147 }
4148
5fee5ec3 4149 static void testDate(Date date, int year, Date expected)
b4c522fa
IB
4150 {
4151 date.year = year;
4152 assert(date == expected);
4153 }
4154
4155 assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1));
4156
4157 testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1));
4158 testDate(Date(1, 1, 1), 0, Date(0, 1, 1));
4159 testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1));
4160
4161 const cdate = Date(1999, 7, 6);
4162 immutable idate = Date(1999, 7, 6);
4163 static assert(!__traits(compiles, cdate.year = 1999));
4164 static assert(!__traits(compiles, idate.year = 1999));
4165 }
4166
4167
4168 /++
4169 Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4170
4171 Throws:
5fee5ec3 4172 $(REF DateTimeException,std,datetime,date) if `isAD` is true.
b4c522fa
IB
4173 +/
4174 @property ushort yearBC() const @safe pure
4175 {
4176 import std.format : format;
4177
4178 if (isAD)
4179 throw new DateTimeException(format("Year %s is A.D.", _year));
4180 return cast(ushort)((_year * -1) + 1);
4181 }
4182
4183 ///
4184 @safe unittest
4185 {
4186 assert(Date(0, 1, 1).yearBC == 1);
4187 assert(Date(-1, 1, 1).yearBC == 2);
4188 assert(Date(-100, 1, 1).yearBC == 101);
4189 }
4190
4191 @safe unittest
4192 {
5fee5ec3 4193 assertThrown!DateTimeException((Date date){date.yearBC;}(Date(1, 1, 1)));
b4c522fa
IB
4194
4195 auto date = Date(0, 7, 6);
4196 const cdate = Date(0, 7, 6);
4197 immutable idate = Date(0, 7, 6);
4198 assert(date.yearBC == 1);
4199 assert(cdate.yearBC == 1);
4200 assert(idate.yearBC == 1);
4201 }
4202
4203
4204 /++
4205 Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C.
4206
4207 Params:
4208 year = The year B.C. to set this $(LREF Date)'s year to.
4209
4210 Throws:
4211 $(REF DateTimeException,std,datetime,date) if a non-positive value
4212 is given.
4213 +/
4214 @property void yearBC(int year) @safe pure
4215 {
4216 if (year <= 0)
4217 throw new DateTimeException("The given year is not a year B.C.");
4218 _year = cast(short)((year - 1) * -1);
4219 }
4220
4221 ///
4222 @safe unittest
4223 {
4224 auto date = Date(2010, 1, 1);
4225 date.yearBC = 1;
4226 assert(date == Date(0, 1, 1));
4227
4228 date.yearBC = 10;
4229 assert(date == Date(-9, 1, 1));
4230 }
4231
4232 @safe unittest
4233 {
4234 assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1)));
4235
4236 auto date = Date(0, 7, 6);
4237 const cdate = Date(0, 7, 6);
4238 immutable idate = Date(0, 7, 6);
4239 date.yearBC = 7;
4240 assert(date.yearBC == 7);
4241 static assert(!__traits(compiles, cdate.yearBC = 7));
4242 static assert(!__traits(compiles, idate.yearBC = 7));
4243 }
4244
4245
4246 /++
4247 Month of a Gregorian Year.
4248 +/
4249 @property Month month() const @safe pure nothrow @nogc
4250 {
4251 return _month;
4252 }
4253
4254 ///
4255 @safe unittest
4256 {
4257 assert(Date(1999, 7, 6).month == 7);
4258 assert(Date(2010, 10, 4).month == 10);
4259 assert(Date(-7, 4, 5).month == 4);
4260 }
4261
4262 @safe unittest
4263 {
4264 assert(Date.init.month == 1);
4265 assert(Date(1999, 7, 6).month == 7);
4266 assert(Date(-1999, 7, 6).month == 7);
4267
4268 const cdate = Date(1999, 7, 6);
4269 immutable idate = Date(1999, 7, 6);
4270 assert(cdate.month == 7);
4271 assert(idate.month == 7);
4272 }
4273
4274 /++
4275 Month of a Gregorian Year.
4276
4277 Params:
4278 month = The month to set this $(LREF Date)'s month to.
4279
4280 Throws:
4281 $(REF DateTimeException,std,datetime,date) if the given month is
4282 not a valid month or if the current day would not be valid in the
4283 given month.
4284 +/
4285 @property void month(Month month) @safe pure
4286 {
4287 enforceValid!"months"(month);
4288 enforceValid!"days"(_year, month, _day);
4289 _month = cast(Month) month;
4290 }
4291
4292 @safe unittest
4293 {
5fee5ec3 4294 static void testDate(Date date, Month month, Date expected = Date.init)
b4c522fa
IB
4295 {
4296 date.month = month;
4297 assert(expected != Date.init);
4298 assert(date == expected);
4299 }
4300
4301 assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0));
4302 assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13));
4303 assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2));
4304 assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2));
4305
4306 testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1));
4307 testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1));
4308
4309 const cdate = Date(1999, 7, 6);
4310 immutable idate = Date(1999, 7, 6);
4311 static assert(!__traits(compiles, cdate.month = 7));
4312 static assert(!__traits(compiles, idate.month = 7));
4313 }
4314
4315
4316 /++
4317 Day of a Gregorian Month.
4318 +/
4319 @property ubyte day() const @safe pure nothrow @nogc
4320 {
4321 return _day;
4322 }
4323
4324 ///
4325 @safe unittest
4326 {
4327 assert(Date(1999, 7, 6).day == 6);
4328 assert(Date(2010, 10, 4).day == 4);
4329 assert(Date(-7, 4, 5).day == 5);
4330 }
4331
4332 @safe unittest
4333 {
4334 import std.format : format;
4335 import std.range : chain;
4336
4337 static void test(Date date, int expected)
4338 {
4339 assert(date.day == expected, format("Value given: %s", date));
4340 }
4341
4342 foreach (year; chain(testYearsBC, testYearsAD))
4343 {
4344 foreach (md; testMonthDays)
4345 test(Date(year, md.month, md.day), md.day);
4346 }
4347
4348 const cdate = Date(1999, 7, 6);
4349 immutable idate = Date(1999, 7, 6);
4350 assert(cdate.day == 6);
4351 assert(idate.day == 6);
4352 }
4353
4354 /++
4355 Day of a Gregorian Month.
4356
4357 Params:
4358 day = The day of the month to set this $(LREF Date)'s day to.
4359
4360 Throws:
4361 $(REF DateTimeException,std,datetime,date) if the given day is not
4362 a valid day of the current month.
4363 +/
4364 @property void day(int day) @safe pure
4365 {
4366 enforceValid!"days"(_year, _month, day);
4367 _day = cast(ubyte) day;
4368 }
4369
4370 @safe unittest
4371 {
4372 import std.exception : assertNotThrown;
4373
4374 static void testDate(Date date, int day)
4375 {
4376 date.day = day;
4377 }
4378
4379 // Test A.D.
4380 assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0));
4381 assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32));
4382 assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29));
4383 assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30));
4384 assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32));
4385 assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31));
4386 assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32));
4387 assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31));
4388 assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32));
4389 assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32));
4390 assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31));
4391 assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32));
4392 assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31));
4393 assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32));
4394
4395 assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31));
4396 assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28));
4397 assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29));
4398 assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31));
4399 assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30));
4400 assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31));
4401 assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30));
4402 assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31));
4403 assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31));
4404 assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30));
4405 assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31));
4406 assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30));
4407 assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31));
4408
4409 {
4410 auto date = Date(1, 1, 1);
4411 date.day = 6;
4412 assert(date == Date(1, 1, 6));
4413 }
4414
4415 // Test B.C.
4416 assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0));
4417 assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32));
4418 assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29));
4419 assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30));
4420 assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32));
4421 assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31));
4422 assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32));
4423 assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31));
4424 assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32));
4425 assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32));
4426 assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31));
4427 assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32));
4428 assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31));
4429 assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32));
4430
4431 assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31));
4432 assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28));
4433 assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29));
4434 assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31));
4435 assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30));
4436 assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31));
4437 assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30));
4438 assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31));
4439 assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31));
4440 assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30));
4441 assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31));
4442 assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30));
4443 assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31));
4444
4445 {
4446 auto date = Date(-1, 1, 1);
4447 date.day = 6;
4448 assert(date == Date(-1, 1, 6));
4449 }
4450
4451 const cdate = Date(1999, 7, 6);
4452 immutable idate = Date(1999, 7, 6);
4453 static assert(!__traits(compiles, cdate.day = 6));
4454 static assert(!__traits(compiles, idate.day = 6));
4455 }
4456
4457
4458 /++
5fee5ec3
IB
4459 Adds the given number of years or months to this $(LREF Date), mutating
4460 it. A negative number will subtract.
b4c522fa
IB
4461
4462 Note that if day overflow is allowed, and the date with the adjusted
4463 year/month overflows the number of days in the new month, then the month
4464 will be incremented by one, and the day set to the number of days
4465 overflowed. (e.g. if the day were 31 and the new month were June, then
4466 the month would be incremented to July, and the new day would be 1). If
4467 day overflow is not allowed, then the day will be set to the last valid
4468 day in the month (e.g. June 31st would become June 30th).
4469
4470 Params:
4471 units = The type of units to add ("years" or "months").
4472 value = The number of months or years to add to this
4473 $(LREF Date).
4474 allowOverflow = Whether the day should be allowed to overflow,
4475 causing the month to increment.
5fee5ec3
IB
4476
4477 Returns:
4478 A reference to the `Date` (`this`).
b4c522fa
IB
4479 +/
4480 @safe pure nothrow @nogc
4481 ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4482 if (units == "years")
4483 {
4484 _year += value;
4485
4486 if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year))
4487 {
4488 if (allowOverflow == AllowDayOverflow.yes)
4489 {
4490 _month = Month.mar;
4491 _day = 1;
4492 }
4493 else
4494 _day = 28;
4495 }
4496
4497 return this;
4498 }
4499
4500 ///
4501 @safe unittest
4502 {
4503 auto d1 = Date(2010, 1, 1);
4504 d1.add!"months"(11);
4505 assert(d1 == Date(2010, 12, 1));
4506
4507 auto d2 = Date(2010, 1, 1);
4508 d2.add!"months"(-11);
4509 assert(d2 == Date(2009, 2, 1));
4510
4511 auto d3 = Date(2000, 2, 29);
4512 d3.add!"years"(1);
4513 assert(d3 == Date(2001, 3, 1));
4514
4515 auto d4 = Date(2000, 2, 29);
4516 d4.add!"years"(1, AllowDayOverflow.no);
4517 assert(d4 == Date(2001, 2, 28));
4518 }
4519
4520 // Test add!"years"() with AllowDayOverflow.yes
4521 @safe unittest
4522 {
4523 // Test A.D.
4524 {
4525 auto date = Date(1999, 7, 6);
4526 date.add!"years"(7);
4527 assert(date == Date(2006, 7, 6));
4528 date.add!"years"(-9);
4529 assert(date == Date(1997, 7, 6));
4530 }
4531
4532 {
4533 auto date = Date(1999, 2, 28);
4534 date.add!"years"(1);
4535 assert(date == Date(2000, 2, 28));
4536 }
4537
4538 {
4539 auto date = Date(2000, 2, 29);
4540 date.add!"years"(-1);
4541 assert(date == Date(1999, 3, 1));
4542 }
4543
4544 // Test B.C.
4545 {
4546 auto date = Date(-1999, 7, 6);
4547 date.add!"years"(-7);
4548 assert(date == Date(-2006, 7, 6));
4549 date.add!"years"(9);
4550 assert(date == Date(-1997, 7, 6));
4551 }
4552
4553 {
4554 auto date = Date(-1999, 2, 28);
4555 date.add!"years"(-1);
4556 assert(date == Date(-2000, 2, 28));
4557 }
4558
4559 {
4560 auto date = Date(-2000, 2, 29);
4561 date.add!"years"(1);
4562 assert(date == Date(-1999, 3, 1));
4563 }
4564
4565 // Test Both
4566 {
4567 auto date = Date(4, 7, 6);
4568 date.add!"years"(-5);
4569 assert(date == Date(-1, 7, 6));
4570 date.add!"years"(5);
4571 assert(date == Date(4, 7, 6));
4572 }
4573
4574 {
4575 auto date = Date(-4, 7, 6);
4576 date.add!"years"(5);
4577 assert(date == Date(1, 7, 6));
4578 date.add!"years"(-5);
4579 assert(date == Date(-4, 7, 6));
4580 }
4581
4582 {
4583 auto date = Date(4, 7, 6);
4584 date.add!"years"(-8);
4585 assert(date == Date(-4, 7, 6));
4586 date.add!"years"(8);
4587 assert(date == Date(4, 7, 6));
4588 }
4589
4590 {
4591 auto date = Date(-4, 7, 6);
4592 date.add!"years"(8);
4593 assert(date == Date(4, 7, 6));
4594 date.add!"years"(-8);
4595 assert(date == Date(-4, 7, 6));
4596 }
4597
4598 {
4599 auto date = Date(-4, 2, 29);
4600 date.add!"years"(5);
4601 assert(date == Date(1, 3, 1));
4602 }
4603
4604 {
4605 auto date = Date(4, 2, 29);
4606 date.add!"years"(-5);
4607 assert(date == Date(-1, 3, 1));
4608 }
4609
4610 {
4611 auto date = Date(4, 2, 29);
4612 date.add!"years"(-5).add!"years"(7);
4613 assert(date == Date(6, 3, 1));
4614 }
4615
4616 const cdate = Date(1999, 7, 6);
4617 immutable idate = Date(1999, 7, 6);
4618 static assert(!__traits(compiles, cdate.add!"years"(7)));
4619 static assert(!__traits(compiles, idate.add!"years"(7)));
4620 }
4621
4622 // Test add!"years"() with AllowDayOverflow.no
4623 @safe unittest
4624 {
4625 // Test A.D.
4626 {
4627 auto date = Date(1999, 7, 6);
4628 date.add!"years"(7, AllowDayOverflow.no);
4629 assert(date == Date(2006, 7, 6));
4630 date.add!"years"(-9, AllowDayOverflow.no);
4631 assert(date == Date(1997, 7, 6));
4632 }
4633
4634 {
4635 auto date = Date(1999, 2, 28);
4636 date.add!"years"(1, AllowDayOverflow.no);
4637 assert(date == Date(2000, 2, 28));
4638 }
4639
4640 {
4641 auto date = Date(2000, 2, 29);
4642 date.add!"years"(-1, AllowDayOverflow.no);
4643 assert(date == Date(1999, 2, 28));
4644 }
4645
4646 // Test B.C.
4647 {
4648 auto date = Date(-1999, 7, 6);
4649 date.add!"years"(-7, AllowDayOverflow.no);
4650 assert(date == Date(-2006, 7, 6));
4651 date.add!"years"(9, AllowDayOverflow.no);
4652 assert(date == Date(-1997, 7, 6));
4653 }
4654
4655 {
4656 auto date = Date(-1999, 2, 28);
4657 date.add!"years"(-1, AllowDayOverflow.no);
4658 assert(date == Date(-2000, 2, 28));
4659 }
4660
4661 {
4662 auto date = Date(-2000, 2, 29);
4663 date.add!"years"(1, AllowDayOverflow.no);
4664 assert(date == Date(-1999, 2, 28));
4665 }
4666
4667 // Test Both
4668 {
4669 auto date = Date(4, 7, 6);
4670 date.add!"years"(-5, AllowDayOverflow.no);
4671 assert(date == Date(-1, 7, 6));
4672 date.add!"years"(5, AllowDayOverflow.no);
4673 assert(date == Date(4, 7, 6));
4674 }
4675
4676 {
4677 auto date = Date(-4, 7, 6);
4678 date.add!"years"(5, AllowDayOverflow.no);
4679 assert(date == Date(1, 7, 6));
4680 date.add!"years"(-5, AllowDayOverflow.no);
4681 assert(date == Date(-4, 7, 6));
4682 }
4683
4684 {
4685 auto date = Date(4, 7, 6);
4686 date.add!"years"(-8, AllowDayOverflow.no);
4687 assert(date == Date(-4, 7, 6));
4688 date.add!"years"(8, AllowDayOverflow.no);
4689 assert(date == Date(4, 7, 6));
4690 }
4691
4692 {
4693 auto date = Date(-4, 7, 6);
4694 date.add!"years"(8, AllowDayOverflow.no);
4695 assert(date == Date(4, 7, 6));
4696 date.add!"years"(-8, AllowDayOverflow.no);
4697 assert(date == Date(-4, 7, 6));
4698 }
4699
4700 {
4701 auto date = Date(-4, 2, 29);
4702 date.add!"years"(5, AllowDayOverflow.no);
4703 assert(date == Date(1, 2, 28));
4704 }
4705
4706 {
4707 auto date = Date(4, 2, 29);
4708 date.add!"years"(-5, AllowDayOverflow.no);
4709 assert(date == Date(-1, 2, 28));
4710 }
4711
4712 {
4713 auto date = Date(4, 2, 29);
4714 date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no);
4715 assert(date == Date(6, 2, 28));
4716 }
4717 }
4718
4719
4720 // Shares documentation with "years" version.
4721 @safe pure nothrow @nogc
4722 ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
4723 if (units == "months")
4724 {
4725 auto years = months / 12;
4726 months %= 12;
4727 auto newMonth = _month + months;
4728
4729 if (months < 0)
4730 {
4731 if (newMonth < 1)
4732 {
4733 newMonth += 12;
4734 --years;
4735 }
4736 }
4737 else if (newMonth > 12)
4738 {
4739 newMonth -= 12;
4740 ++years;
4741 }
4742
4743 _year += years;
4744 _month = cast(Month) newMonth;
4745
4746 immutable currMaxDay = maxDay(_year, _month);
4747 immutable overflow = _day - currMaxDay;
4748
4749 if (overflow > 0)
4750 {
4751 if (allowOverflow == AllowDayOverflow.yes)
4752 {
4753 ++_month;
4754 _day = cast(ubyte) overflow;
4755 }
4756 else
4757 _day = cast(ubyte) currMaxDay;
4758 }
4759
4760 return this;
4761 }
4762
4763 // Test add!"months"() with AllowDayOverflow.yes
4764 @safe unittest
4765 {
4766 // Test A.D.
4767 {
4768 auto date = Date(1999, 7, 6);
4769 date.add!"months"(3);
4770 assert(date == Date(1999, 10, 6));
4771 date.add!"months"(-4);
4772 assert(date == Date(1999, 6, 6));
4773 }
4774
4775 {
4776 auto date = Date(1999, 7, 6);
4777 date.add!"months"(6);
4778 assert(date == Date(2000, 1, 6));
4779 date.add!"months"(-6);
4780 assert(date == Date(1999, 7, 6));
4781 }
4782
4783 {
4784 auto date = Date(1999, 7, 6);
4785 date.add!"months"(27);
4786 assert(date == Date(2001, 10, 6));
4787 date.add!"months"(-28);
4788 assert(date == Date(1999, 6, 6));
4789 }
4790
4791 {
4792 auto date = Date(1999, 5, 31);
4793 date.add!"months"(1);
4794 assert(date == Date(1999, 7, 1));
4795 }
4796
4797 {
4798 auto date = Date(1999, 5, 31);
4799 date.add!"months"(-1);
4800 assert(date == Date(1999, 5, 1));
4801 }
4802
4803 {
4804 auto date = Date(1999, 2, 28);
4805 date.add!"months"(12);
4806 assert(date == Date(2000, 2, 28));
4807 }
4808
4809 {
4810 auto date = Date(2000, 2, 29);
4811 date.add!"months"(12);
4812 assert(date == Date(2001, 3, 1));
4813 }
4814
4815 {
4816 auto date = Date(1999, 7, 31);
4817 date.add!"months"(1);
4818 assert(date == Date(1999, 8, 31));
4819 date.add!"months"(1);
4820 assert(date == Date(1999, 10, 1));
4821 }
4822
4823 {
4824 auto date = Date(1998, 8, 31);
4825 date.add!"months"(13);
4826 assert(date == Date(1999, 10, 1));
4827 date.add!"months"(-13);
4828 assert(date == Date(1998, 9, 1));
4829 }
4830
4831 {
4832 auto date = Date(1997, 12, 31);
4833 date.add!"months"(13);
4834 assert(date == Date(1999, 1, 31));
4835 date.add!"months"(-13);
4836 assert(date == Date(1997, 12, 31));
4837 }
4838
4839 {
4840 auto date = Date(1997, 12, 31);
4841 date.add!"months"(14);
4842 assert(date == Date(1999, 3, 3));
4843 date.add!"months"(-14);
4844 assert(date == Date(1998, 1, 3));
4845 }
4846
4847 {
4848 auto date = Date(1998, 12, 31);
4849 date.add!"months"(14);
4850 assert(date == Date(2000, 3, 2));
4851 date.add!"months"(-14);
4852 assert(date == Date(1999, 1, 2));
4853 }
4854
4855 {
4856 auto date = Date(1999, 12, 31);
4857 date.add!"months"(14);
4858 assert(date == Date(2001, 3, 3));
4859 date.add!"months"(-14);
4860 assert(date == Date(2000, 1, 3));
4861 }
4862
4863 // Test B.C.
4864 {
4865 auto date = Date(-1999, 7, 6);
4866 date.add!"months"(3);
4867 assert(date == Date(-1999, 10, 6));
4868 date.add!"months"(-4);
4869 assert(date == Date(-1999, 6, 6));
4870 }
4871
4872 {
4873 auto date = Date(-1999, 7, 6);
4874 date.add!"months"(6);
4875 assert(date == Date(-1998, 1, 6));
4876 date.add!"months"(-6);
4877 assert(date == Date(-1999, 7, 6));
4878 }
4879
4880 {
4881 auto date = Date(-1999, 7, 6);
4882 date.add!"months"(-27);
4883 assert(date == Date(-2001, 4, 6));
4884 date.add!"months"(28);
4885 assert(date == Date(-1999, 8, 6));
4886 }
4887
4888 {
4889 auto date = Date(-1999, 5, 31);
4890 date.add!"months"(1);
4891 assert(date == Date(-1999, 7, 1));
4892 }
4893
4894 {
4895 auto date = Date(-1999, 5, 31);
4896 date.add!"months"(-1);
4897 assert(date == Date(-1999, 5, 1));
4898 }
4899
4900 {
4901 auto date = Date(-1999, 2, 28);
4902 date.add!"months"(-12);
4903 assert(date == Date(-2000, 2, 28));
4904 }
4905
4906 {
4907 auto date = Date(-2000, 2, 29);
4908 date.add!"months"(-12);
4909 assert(date == Date(-2001, 3, 1));
4910 }
4911
4912 {
4913 auto date = Date(-1999, 7, 31);
4914 date.add!"months"(1);
4915 assert(date == Date(-1999, 8, 31));
4916 date.add!"months"(1);
4917 assert(date == Date(-1999, 10, 1));
4918 }
4919
4920 {
4921 auto date = Date(-1998, 8, 31);
4922 date.add!"months"(13);
4923 assert(date == Date(-1997, 10, 1));
4924 date.add!"months"(-13);
4925 assert(date == Date(-1998, 9, 1));
4926 }
4927
4928 {
4929 auto date = Date(-1997, 12, 31);
4930 date.add!"months"(13);
4931 assert(date == Date(-1995, 1, 31));
4932 date.add!"months"(-13);
4933 assert(date == Date(-1997, 12, 31));
4934 }
4935
4936 {
4937 auto date = Date(-1997, 12, 31);
4938 date.add!"months"(14);
4939 assert(date == Date(-1995, 3, 3));
4940 date.add!"months"(-14);
4941 assert(date == Date(-1996, 1, 3));
4942 }
4943
4944 {
4945 auto date = Date(-2002, 12, 31);
4946 date.add!"months"(14);
4947 assert(date == Date(-2000, 3, 2));
4948 date.add!"months"(-14);
4949 assert(date == Date(-2001, 1, 2));
4950 }
4951
4952 {
4953 auto date = Date(-2001, 12, 31);
4954 date.add!"months"(14);
4955 assert(date == Date(-1999, 3, 3));
4956 date.add!"months"(-14);
4957 assert(date == Date(-2000, 1, 3));
4958 }
4959
4960 // Test Both
4961 {
4962 auto date = Date(1, 1, 1);
4963 date.add!"months"(-1);
4964 assert(date == Date(0, 12, 1));
4965 date.add!"months"(1);
4966 assert(date == Date(1, 1, 1));
4967 }
4968
4969 {
4970 auto date = Date(4, 1, 1);
4971 date.add!"months"(-48);
4972 assert(date == Date(0, 1, 1));
4973 date.add!"months"(48);
4974 assert(date == Date(4, 1, 1));
4975 }
4976
4977 {
4978 auto date = Date(4, 3, 31);
4979 date.add!"months"(-49);
4980 assert(date == Date(0, 3, 2));
4981 date.add!"months"(49);
4982 assert(date == Date(4, 4, 2));
4983 }
4984
4985 {
4986 auto date = Date(4, 3, 31);
4987 date.add!"months"(-85);
4988 assert(date == Date(-3, 3, 3));
4989 date.add!"months"(85);
4990 assert(date == Date(4, 4, 3));
4991 }
4992
4993 {
4994 auto date = Date(-3, 3, 31);
4995 date.add!"months"(85).add!"months"(-83);
4996 assert(date == Date(-3, 6, 1));
4997 }
4998
4999 const cdate = Date(1999, 7, 6);
5000 immutable idate = Date(1999, 7, 6);
5001 static assert(!__traits(compiles, cdate.add!"months"(3)));
5002 static assert(!__traits(compiles, idate.add!"months"(3)));
5003 }
5004
5005 // Test add!"months"() with AllowDayOverflow.no
5006 @safe unittest
5007 {
5008 // Test A.D.
5009 {
5010 auto date = Date(1999, 7, 6);
5011 date.add!"months"(3, AllowDayOverflow.no);
5012 assert(date == Date(1999, 10, 6));
5013 date.add!"months"(-4, AllowDayOverflow.no);
5014 assert(date == Date(1999, 6, 6));
5015 }
5016
5017 {
5018 auto date = Date(1999, 7, 6);
5019 date.add!"months"(6, AllowDayOverflow.no);
5020 assert(date == Date(2000, 1, 6));
5021 date.add!"months"(-6, AllowDayOverflow.no);
5022 assert(date == Date(1999, 7, 6));
5023 }
5024
5025 {
5026 auto date = Date(1999, 7, 6);
5027 date.add!"months"(27, AllowDayOverflow.no);
5028 assert(date == Date(2001, 10, 6));
5029 date.add!"months"(-28, AllowDayOverflow.no);
5030 assert(date == Date(1999, 6, 6));
5031 }
5032
5033 {
5034 auto date = Date(1999, 5, 31);
5035 date.add!"months"(1, AllowDayOverflow.no);
5036 assert(date == Date(1999, 6, 30));
5037 }
5038
5039 {
5040 auto date = Date(1999, 5, 31);
5041 date.add!"months"(-1, AllowDayOverflow.no);
5042 assert(date == Date(1999, 4, 30));
5043 }
5044
5045 {
5046 auto date = Date(1999, 2, 28);
5047 date.add!"months"(12, AllowDayOverflow.no);
5048 assert(date == Date(2000, 2, 28));
5049 }
5050
5051 {
5052 auto date = Date(2000, 2, 29);
5053 date.add!"months"(12, AllowDayOverflow.no);
5054 assert(date == Date(2001, 2, 28));
5055 }
5056
5057 {
5058 auto date = Date(1999, 7, 31);
5059 date.add!"months"(1, AllowDayOverflow.no);
5060 assert(date == Date(1999, 8, 31));
5061 date.add!"months"(1, AllowDayOverflow.no);
5062 assert(date == Date(1999, 9, 30));
5063 }
5064
5065 {
5066 auto date = Date(1998, 8, 31);
5067 date.add!"months"(13, AllowDayOverflow.no);
5068 assert(date == Date(1999, 9, 30));
5069 date.add!"months"(-13, AllowDayOverflow.no);
5070 assert(date == Date(1998, 8, 30));
5071 }
5072
5073 {
5074 auto date = Date(1997, 12, 31);
5075 date.add!"months"(13, AllowDayOverflow.no);
5076 assert(date == Date(1999, 1, 31));
5077 date.add!"months"(-13, AllowDayOverflow.no);
5078 assert(date == Date(1997, 12, 31));
5079 }
5080
5081 {
5082 auto date = Date(1997, 12, 31);
5083 date.add!"months"(14, AllowDayOverflow.no);
5084 assert(date == Date(1999, 2, 28));
5085 date.add!"months"(-14, AllowDayOverflow.no);
5086 assert(date == Date(1997, 12, 28));
5087 }
5088
5089 {
5090 auto date = Date(1998, 12, 31);
5091 date.add!"months"(14, AllowDayOverflow.no);
5092 assert(date == Date(2000, 2, 29));
5093 date.add!"months"(-14, AllowDayOverflow.no);
5094 assert(date == Date(1998, 12, 29));
5095 }
5096
5097 {
5098 auto date = Date(1999, 12, 31);
5099 date.add!"months"(14, AllowDayOverflow.no);
5100 assert(date == Date(2001, 2, 28));
5101 date.add!"months"(-14, AllowDayOverflow.no);
5102 assert(date == Date(1999, 12, 28));
5103 }
5104
5105 // Test B.C.
5106 {
5107 auto date = Date(-1999, 7, 6);
5108 date.add!"months"(3, AllowDayOverflow.no);
5109 assert(date == Date(-1999, 10, 6));
5110 date.add!"months"(-4, AllowDayOverflow.no);
5111 assert(date == Date(-1999, 6, 6));
5112 }
5113
5114 {
5115 auto date = Date(-1999, 7, 6);
5116 date.add!"months"(6, AllowDayOverflow.no);
5117 assert(date == Date(-1998, 1, 6));
5118 date.add!"months"(-6, AllowDayOverflow.no);
5119 assert(date == Date(-1999, 7, 6));
5120 }
5121
5122 {
5123 auto date = Date(-1999, 7, 6);
5124 date.add!"months"(-27, AllowDayOverflow.no);
5125 assert(date == Date(-2001, 4, 6));
5126 date.add!"months"(28, AllowDayOverflow.no);
5127 assert(date == Date(-1999, 8, 6));
5128 }
5129
5130 {
5131 auto date = Date(-1999, 5, 31);
5132 date.add!"months"(1, AllowDayOverflow.no);
5133 assert(date == Date(-1999, 6, 30));
5134 }
5135
5136 {
5137 auto date = Date(-1999, 5, 31);
5138 date.add!"months"(-1, AllowDayOverflow.no);
5139 assert(date == Date(-1999, 4, 30));
5140 }
5141
5142 {
5143 auto date = Date(-1999, 2, 28);
5144 date.add!"months"(-12, AllowDayOverflow.no);
5145 assert(date == Date(-2000, 2, 28));
5146 }
5147
5148 {
5149 auto date = Date(-2000, 2, 29);
5150 date.add!"months"(-12, AllowDayOverflow.no);
5151 assert(date == Date(-2001, 2, 28));
5152 }
5153
5154 {
5155 auto date = Date(-1999, 7, 31);
5156 date.add!"months"(1, AllowDayOverflow.no);
5157 assert(date == Date(-1999, 8, 31));
5158 date.add!"months"(1, AllowDayOverflow.no);
5159 assert(date == Date(-1999, 9, 30));
5160 }
5161
5162 {
5163 auto date = Date(-1998, 8, 31);
5164 date.add!"months"(13, AllowDayOverflow.no);
5165 assert(date == Date(-1997, 9, 30));
5166 date.add!"months"(-13, AllowDayOverflow.no);
5167 assert(date == Date(-1998, 8, 30));
5168 }
5169
5170 {
5171 auto date = Date(-1997, 12, 31);
5172 date.add!"months"(13, AllowDayOverflow.no);
5173 assert(date == Date(-1995, 1, 31));
5174 date.add!"months"(-13, AllowDayOverflow.no);
5175 assert(date == Date(-1997, 12, 31));
5176 }
5177
5178 {
5179 auto date = Date(-1997, 12, 31);
5180 date.add!"months"(14, AllowDayOverflow.no);
5181 assert(date == Date(-1995, 2, 28));
5182 date.add!"months"(-14, AllowDayOverflow.no);
5183 assert(date == Date(-1997, 12, 28));
5184 }
5185
5186 {
5187 auto date = Date(-2002, 12, 31);
5188 date.add!"months"(14, AllowDayOverflow.no);
5189 assert(date == Date(-2000, 2, 29));
5190 date.add!"months"(-14, AllowDayOverflow.no);
5191 assert(date == Date(-2002, 12, 29));
5192 }
5193
5194 {
5195 auto date = Date(-2001, 12, 31);
5196 date.add!"months"(14, AllowDayOverflow.no);
5197 assert(date == Date(-1999, 2, 28));
5198 date.add!"months"(-14, AllowDayOverflow.no);
5199 assert(date == Date(-2001, 12, 28));
5200 }
5201
5202 // Test Both
5203 {
5204 auto date = Date(1, 1, 1);
5205 date.add!"months"(-1, AllowDayOverflow.no);
5206 assert(date == Date(0, 12, 1));
5207 date.add!"months"(1, AllowDayOverflow.no);
5208 assert(date == Date(1, 1, 1));
5209 }
5210
5211 {
5212 auto date = Date(4, 1, 1);
5213 date.add!"months"(-48, AllowDayOverflow.no);
5214 assert(date == Date(0, 1, 1));
5215 date.add!"months"(48, AllowDayOverflow.no);
5216 assert(date == Date(4, 1, 1));
5217 }
5218
5219 {
5220 auto date = Date(4, 3, 31);
5221 date.add!"months"(-49, AllowDayOverflow.no);
5222 assert(date == Date(0, 2, 29));
5223 date.add!"months"(49, AllowDayOverflow.no);
5224 assert(date == Date(4, 3, 29));
5225 }
5226
5227 {
5228 auto date = Date(4, 3, 31);
5229 date.add!"months"(-85, AllowDayOverflow.no);
5230 assert(date == Date(-3, 2, 28));
5231 date.add!"months"(85, AllowDayOverflow.no);
5232 assert(date == Date(4, 3, 28));
5233 }
5234
5235 {
5236 auto date = Date(-3, 3, 31);
5237 date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no);
5238 assert(date == Date(-3, 5, 30));
5239 }
5240 }
5241
5242
5243 /++
5fee5ec3
IB
5244 Adds the given number of years or months to this $(LREF Date), mutating
5245 it. A negative number will subtract.
b4c522fa
IB
5246
5247 The difference between rolling and adding is that rolling does not
5248 affect larger units. Rolling a $(LREF Date) 12 months gets
5249 the exact same $(LREF Date). However, the days can still be affected due
5250 to the differing number of days in each month.
5251
5252 Because there are no units larger than years, there is no difference
5253 between adding and rolling years.
5254
5255 Params:
5256 units = The type of units to add ("years" or "months").
5257 value = The number of months or years to add to this
5258 $(LREF Date).
5259 allowOverflow = Whether the day should be allowed to overflow,
5260 causing the month to increment.
5fee5ec3
IB
5261
5262 Returns:
5263 A reference to the `Date` (`this`).
b4c522fa
IB
5264 +/
5265 @safe pure nothrow @nogc
5266 ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5267 if (units == "years")
5268 {
5269 return add!"years"(value, allowOverflow);
5270 }
5271
5272 ///
5273 @safe unittest
5274 {
5275 auto d1 = Date(2010, 1, 1);
5276 d1.roll!"months"(1);
5277 assert(d1 == Date(2010, 2, 1));
5278
5279 auto d2 = Date(2010, 1, 1);
5280 d2.roll!"months"(-1);
5281 assert(d2 == Date(2010, 12, 1));
5282
5283 auto d3 = Date(1999, 1, 29);
5284 d3.roll!"months"(1);
5285 assert(d3 == Date(1999, 3, 1));
5286
5287 auto d4 = Date(1999, 1, 29);
5288 d4.roll!"months"(1, AllowDayOverflow.no);
5289 assert(d4 == Date(1999, 2, 28));
5290
5291 auto d5 = Date(2000, 2, 29);
5292 d5.roll!"years"(1);
5293 assert(d5 == Date(2001, 3, 1));
5294
5295 auto d6 = Date(2000, 2, 29);
5296 d6.roll!"years"(1, AllowDayOverflow.no);
5297 assert(d6 == Date(2001, 2, 28));
5298 }
5299
5300 @safe unittest
5301 {
5302 const cdate = Date(1999, 7, 6);
5303 immutable idate = Date(1999, 7, 6);
5304 static assert(!__traits(compiles, cdate.roll!"years"(3)));
5305 static assert(!__traits(compiles, idate.rolYears(3)));
5306 }
5307
5308
5309 // Shares documentation with "years" version.
5310 @safe pure nothrow @nogc
5311 ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
5312 if (units == "months")
5313 {
5314 months %= 12;
5315 auto newMonth = _month + months;
5316
5317 if (months < 0)
5318 {
5319 if (newMonth < 1)
5320 newMonth += 12;
5321 }
5322 else
5323 {
5324 if (newMonth > 12)
5325 newMonth -= 12;
5326 }
5327
5328 _month = cast(Month) newMonth;
5329
5330 immutable currMaxDay = maxDay(_year, _month);
5331 immutable overflow = _day - currMaxDay;
5332
5333 if (overflow > 0)
5334 {
5335 if (allowOverflow == AllowDayOverflow.yes)
5336 {
5337 ++_month;
5338 _day = cast(ubyte) overflow;
5339 }
5340 else
5341 _day = cast(ubyte) currMaxDay;
5342 }
5343
5344 return this;
5345 }
5346
5347 // Test roll!"months"() with AllowDayOverflow.yes
5348 @safe unittest
5349 {
5350 // Test A.D.
5351 {
5352 auto date = Date(1999, 7, 6);
5353 date.roll!"months"(3);
5354 assert(date == Date(1999, 10, 6));
5355 date.roll!"months"(-4);
5356 assert(date == Date(1999, 6, 6));
5357 }
5358
5359 {
5360 auto date = Date(1999, 7, 6);
5361 date.roll!"months"(6);
5362 assert(date == Date(1999, 1, 6));
5363 date.roll!"months"(-6);
5364 assert(date == Date(1999, 7, 6));
5365 }
5366
5367 {
5368 auto date = Date(1999, 7, 6);
5369 date.roll!"months"(27);
5370 assert(date == Date(1999, 10, 6));
5371 date.roll!"months"(-28);
5372 assert(date == Date(1999, 6, 6));
5373 }
5374
5375 {
5376 auto date = Date(1999, 5, 31);
5377 date.roll!"months"(1);
5378 assert(date == Date(1999, 7, 1));
5379 }
5380
5381 {
5382 auto date = Date(1999, 5, 31);
5383 date.roll!"months"(-1);
5384 assert(date == Date(1999, 5, 1));
5385 }
5386
5387 {
5388 auto date = Date(1999, 2, 28);
5389 date.roll!"months"(12);
5390 assert(date == Date(1999, 2, 28));
5391 }
5392
5393 {
5394 auto date = Date(2000, 2, 29);
5395 date.roll!"months"(12);
5396 assert(date == Date(2000, 2, 29));
5397 }
5398
5399 {
5400 auto date = Date(1999, 7, 31);
5401 date.roll!"months"(1);
5402 assert(date == Date(1999, 8, 31));
5403 date.roll!"months"(1);
5404 assert(date == Date(1999, 10, 1));
5405 }
5406
5407 {
5408 auto date = Date(1998, 8, 31);
5409 date.roll!"months"(13);
5410 assert(date == Date(1998, 10, 1));
5411 date.roll!"months"(-13);
5412 assert(date == Date(1998, 9, 1));
5413 }
5414
5415 {
5416 auto date = Date(1997, 12, 31);
5417 date.roll!"months"(13);
5418 assert(date == Date(1997, 1, 31));
5419 date.roll!"months"(-13);
5420 assert(date == Date(1997, 12, 31));
5421 }
5422
5423 {
5424 auto date = Date(1997, 12, 31);
5425 date.roll!"months"(14);
5426 assert(date == Date(1997, 3, 3));
5427 date.roll!"months"(-14);
5428 assert(date == Date(1997, 1, 3));
5429 }
5430
5431 {
5432 auto date = Date(1998, 12, 31);
5433 date.roll!"months"(14);
5434 assert(date == Date(1998, 3, 3));
5435 date.roll!"months"(-14);
5436 assert(date == Date(1998, 1, 3));
5437 }
5438
5439 {
5440 auto date = Date(1999, 12, 31);
5441 date.roll!"months"(14);
5442 assert(date == Date(1999, 3, 3));
5443 date.roll!"months"(-14);
5444 assert(date == Date(1999, 1, 3));
5445 }
5446
5447 // Test B.C.
5448 {
5449 auto date = Date(-1999, 7, 6);
5450 date.roll!"months"(3);
5451 assert(date == Date(-1999, 10, 6));
5452 date.roll!"months"(-4);
5453 assert(date == Date(-1999, 6, 6));
5454 }
5455
5456 {
5457 auto date = Date(-1999, 7, 6);
5458 date.roll!"months"(6);
5459 assert(date == Date(-1999, 1, 6));
5460 date.roll!"months"(-6);
5461 assert(date == Date(-1999, 7, 6));
5462 }
5463
5464 {
5465 auto date = Date(-1999, 7, 6);
5466 date.roll!"months"(-27);
5467 assert(date == Date(-1999, 4, 6));
5468 date.roll!"months"(28);
5469 assert(date == Date(-1999, 8, 6));
5470 }
5471
5472 {
5473 auto date = Date(-1999, 5, 31);
5474 date.roll!"months"(1);
5475 assert(date == Date(-1999, 7, 1));
5476 }
5477
5478 {
5479 auto date = Date(-1999, 5, 31);
5480 date.roll!"months"(-1);
5481 assert(date == Date(-1999, 5, 1));
5482 }
5483
5484 {
5485 auto date = Date(-1999, 2, 28);
5486 date.roll!"months"(-12);
5487 assert(date == Date(-1999, 2, 28));
5488 }
5489
5490 {
5491 auto date = Date(-2000, 2, 29);
5492 date.roll!"months"(-12);
5493 assert(date == Date(-2000, 2, 29));
5494 }
5495
5496 {
5497 auto date = Date(-1999, 7, 31);
5498 date.roll!"months"(1);
5499 assert(date == Date(-1999, 8, 31));
5500 date.roll!"months"(1);
5501 assert(date == Date(-1999, 10, 1));
5502 }
5503
5504 {
5505 auto date = Date(-1998, 8, 31);
5506 date.roll!"months"(13);
5507 assert(date == Date(-1998, 10, 1));
5508 date.roll!"months"(-13);
5509 assert(date == Date(-1998, 9, 1));
5510 }
5511
5512 {
5513 auto date = Date(-1997, 12, 31);
5514 date.roll!"months"(13);
5515 assert(date == Date(-1997, 1, 31));
5516 date.roll!"months"(-13);
5517 assert(date == Date(-1997, 12, 31));
5518 }
5519
5520 {
5521 auto date = Date(-1997, 12, 31);
5522 date.roll!"months"(14);
5523 assert(date == Date(-1997, 3, 3));
5524 date.roll!"months"(-14);
5525 assert(date == Date(-1997, 1, 3));
5526 }
5527
5528 {
5529 auto date = Date(-2002, 12, 31);
5530 date.roll!"months"(14);
5531 assert(date == Date(-2002, 3, 3));
5532 date.roll!"months"(-14);
5533 assert(date == Date(-2002, 1, 3));
5534 }
5535
5536 {
5537 auto date = Date(-2001, 12, 31);
5538 date.roll!"months"(14);
5539 assert(date == Date(-2001, 3, 3));
5540 date.roll!"months"(-14);
5541 assert(date == Date(-2001, 1, 3));
5542 }
5543
5544 // Test Both
5545 {
5546 auto date = Date(1, 1, 1);
5547 date.roll!"months"(-1);
5548 assert(date == Date(1, 12, 1));
5549 date.roll!"months"(1);
5550 assert(date == Date(1, 1, 1));
5551 }
5552
5553 {
5554 auto date = Date(4, 1, 1);
5555 date.roll!"months"(-48);
5556 assert(date == Date(4, 1, 1));
5557 date.roll!"months"(48);
5558 assert(date == Date(4, 1, 1));
5559 }
5560
5561 {
5562 auto date = Date(4, 3, 31);
5563 date.roll!"months"(-49);
5564 assert(date == Date(4, 3, 2));
5565 date.roll!"months"(49);
5566 assert(date == Date(4, 4, 2));
5567 }
5568
5569 {
5570 auto date = Date(4, 3, 31);
5571 date.roll!"months"(-85);
5572 assert(date == Date(4, 3, 2));
5573 date.roll!"months"(85);
5574 assert(date == Date(4, 4, 2));
5575 }
5576
5577 {
5578 auto date = Date(-1, 1, 1);
5579 date.roll!"months"(-1);
5580 assert(date == Date(-1, 12, 1));
5581 date.roll!"months"(1);
5582 assert(date == Date(-1, 1, 1));
5583 }
5584
5585 {
5586 auto date = Date(-4, 1, 1);
5587 date.roll!"months"(-48);
5588 assert(date == Date(-4, 1, 1));
5589 date.roll!"months"(48);
5590 assert(date == Date(-4, 1, 1));
5591 }
5592
5593 {
5594 auto date = Date(-4, 3, 31);
5595 date.roll!"months"(-49);
5596 assert(date == Date(-4, 3, 2));
5597 date.roll!"months"(49);
5598 assert(date == Date(-4, 4, 2));
5599 }
5600
5601 {
5602 auto date = Date(-4, 3, 31);
5603 date.roll!"months"(-85);
5604 assert(date == Date(-4, 3, 2));
5605 date.roll!"months"(85);
5606 assert(date == Date(-4, 4, 2));
5607 }
5608
5609 {
5610 auto date = Date(-3, 3, 31);
5611 date.roll!"months"(85).roll!"months"(-83);
5612 assert(date == Date(-3, 6, 1));
5613 }
5614
5615 const cdate = Date(1999, 7, 6);
5616 immutable idate = Date(1999, 7, 6);
5617 static assert(!__traits(compiles, cdate.roll!"months"(3)));
5618 static assert(!__traits(compiles, idate.roll!"months"(3)));
5619 }
5620
5621 // Test roll!"months"() with AllowDayOverflow.no
5622 @safe unittest
5623 {
5624 // Test A.D.
5625 {
5626 auto date = Date(1999, 7, 6);
5627 date.roll!"months"(3, AllowDayOverflow.no);
5628 assert(date == Date(1999, 10, 6));
5629 date.roll!"months"(-4, AllowDayOverflow.no);
5630 assert(date == Date(1999, 6, 6));
5631 }
5632
5633 {
5634 auto date = Date(1999, 7, 6);
5635 date.roll!"months"(6, AllowDayOverflow.no);
5636 assert(date == Date(1999, 1, 6));
5637 date.roll!"months"(-6, AllowDayOverflow.no);
5638 assert(date == Date(1999, 7, 6));
5639 }
5640
5641 {
5642 auto date = Date(1999, 7, 6);
5643 date.roll!"months"(27, AllowDayOverflow.no);
5644 assert(date == Date(1999, 10, 6));
5645 date.roll!"months"(-28, AllowDayOverflow.no);
5646 assert(date == Date(1999, 6, 6));
5647 }
5648
5649 {
5650 auto date = Date(1999, 5, 31);
5651 date.roll!"months"(1, AllowDayOverflow.no);
5652 assert(date == Date(1999, 6, 30));
5653 }
5654
5655 {
5656 auto date = Date(1999, 5, 31);
5657 date.roll!"months"(-1, AllowDayOverflow.no);
5658 assert(date == Date(1999, 4, 30));
5659 }
5660
5661 {
5662 auto date = Date(1999, 2, 28);
5663 date.roll!"months"(12, AllowDayOverflow.no);
5664 assert(date == Date(1999, 2, 28));
5665 }
5666
5667 {
5668 auto date = Date(2000, 2, 29);
5669 date.roll!"months"(12, AllowDayOverflow.no);
5670 assert(date == Date(2000, 2, 29));
5671 }
5672
5673 {
5674 auto date = Date(1999, 7, 31);
5675 date.roll!"months"(1, AllowDayOverflow.no);
5676 assert(date == Date(1999, 8, 31));
5677 date.roll!"months"(1, AllowDayOverflow.no);
5678 assert(date == Date(1999, 9, 30));
5679 }
5680
5681 {
5682 auto date = Date(1998, 8, 31);
5683 date.roll!"months"(13, AllowDayOverflow.no);
5684 assert(date == Date(1998, 9, 30));
5685 date.roll!"months"(-13, AllowDayOverflow.no);
5686 assert(date == Date(1998, 8, 30));
5687 }
5688
5689 {
5690 auto date = Date(1997, 12, 31);
5691 date.roll!"months"(13, AllowDayOverflow.no);
5692 assert(date == Date(1997, 1, 31));
5693 date.roll!"months"(-13, AllowDayOverflow.no);
5694 assert(date == Date(1997, 12, 31));
5695 }
5696
5697 {
5698 auto date = Date(1997, 12, 31);
5699 date.roll!"months"(14, AllowDayOverflow.no);
5700 assert(date == Date(1997, 2, 28));
5701 date.roll!"months"(-14, AllowDayOverflow.no);
5702 assert(date == Date(1997, 12, 28));
5703 }
5704
5705 {
5706 auto date = Date(1998, 12, 31);
5707 date.roll!"months"(14, AllowDayOverflow.no);
5708 assert(date == Date(1998, 2, 28));
5709 date.roll!"months"(-14, AllowDayOverflow.no);
5710 assert(date == Date(1998, 12, 28));
5711 }
5712
5713 {
5714 auto date = Date(1999, 12, 31);
5715 date.roll!"months"(14, AllowDayOverflow.no);
5716 assert(date == Date(1999, 2, 28));
5717 date.roll!"months"(-14, AllowDayOverflow.no);
5718 assert(date == Date(1999, 12, 28));
5719 }
5720
5721 // Test B.C.
5722 {
5723 auto date = Date(-1999, 7, 6);
5724 date.roll!"months"(3, AllowDayOverflow.no);
5725 assert(date == Date(-1999, 10, 6));
5726 date.roll!"months"(-4, AllowDayOverflow.no);
5727 assert(date == Date(-1999, 6, 6));
5728 }
5729
5730 {
5731 auto date = Date(-1999, 7, 6);
5732 date.roll!"months"(6, AllowDayOverflow.no);
5733 assert(date == Date(-1999, 1, 6));
5734 date.roll!"months"(-6, AllowDayOverflow.no);
5735 assert(date == Date(-1999, 7, 6));
5736 }
5737
5738 {
5739 auto date = Date(-1999, 7, 6);
5740 date.roll!"months"(-27, AllowDayOverflow.no);
5741 assert(date == Date(-1999, 4, 6));
5742 date.roll!"months"(28, AllowDayOverflow.no);
5743 assert(date == Date(-1999, 8, 6));
5744 }
5745
5746 {
5747 auto date = Date(-1999, 5, 31);
5748 date.roll!"months"(1, AllowDayOverflow.no);
5749 assert(date == Date(-1999, 6, 30));
5750 }
5751
5752 {
5753 auto date = Date(-1999, 5, 31);
5754 date.roll!"months"(-1, AllowDayOverflow.no);
5755 assert(date == Date(-1999, 4, 30));
5756 }
5757
5758 {
5759 auto date = Date(-1999, 2, 28);
5760 date.roll!"months"(-12, AllowDayOverflow.no);
5761 assert(date == Date(-1999, 2, 28));
5762 }
5763
5764 {
5765 auto date = Date(-2000, 2, 29);
5766 date.roll!"months"(-12, AllowDayOverflow.no);
5767 assert(date == Date(-2000, 2, 29));
5768 }
5769
5770 {
5771 auto date = Date(-1999, 7, 31);
5772 date.roll!"months"(1, AllowDayOverflow.no);
5773 assert(date == Date(-1999, 8, 31));
5774 date.roll!"months"(1, AllowDayOverflow.no);
5775 assert(date == Date(-1999, 9, 30));
5776 }
5777
5778 {
5779 auto date = Date(-1998, 8, 31);
5780 date.roll!"months"(13, AllowDayOverflow.no);
5781 assert(date == Date(-1998, 9, 30));
5782 date.roll!"months"(-13, AllowDayOverflow.no);
5783 assert(date == Date(-1998, 8, 30));
5784 }
5785
5786 {
5787 auto date = Date(-1997, 12, 31);
5788 date.roll!"months"(13, AllowDayOverflow.no);
5789 assert(date == Date(-1997, 1, 31));
5790 date.roll!"months"(-13, AllowDayOverflow.no);
5791 assert(date == Date(-1997, 12, 31));
5792 }
5793
5794 {
5795 auto date = Date(-1997, 12, 31);
5796 date.roll!"months"(14, AllowDayOverflow.no);
5797 assert(date == Date(-1997, 2, 28));
5798 date.roll!"months"(-14, AllowDayOverflow.no);
5799 assert(date == Date(-1997, 12, 28));
5800 }
5801
5802 {
5803 auto date = Date(-2002, 12, 31);
5804 date.roll!"months"(14, AllowDayOverflow.no);
5805 assert(date == Date(-2002, 2, 28));
5806 date.roll!"months"(-14, AllowDayOverflow.no);
5807 assert(date == Date(-2002, 12, 28));
5808 }
5809
5810 {
5811 auto date = Date(-2001, 12, 31);
5812 date.roll!"months"(14, AllowDayOverflow.no);
5813 assert(date == Date(-2001, 2, 28));
5814 date.roll!"months"(-14, AllowDayOverflow.no);
5815 assert(date == Date(-2001, 12, 28));
5816 }
5817
5818 // Test Both
5819 {
5820 auto date = Date(1, 1, 1);
5821 date.roll!"months"(-1, AllowDayOverflow.no);
5822 assert(date == Date(1, 12, 1));
5823 date.roll!"months"(1, AllowDayOverflow.no);
5824 assert(date == Date(1, 1, 1));
5825 }
5826
5827 {
5828 auto date = Date(4, 1, 1);
5829 date.roll!"months"(-48, AllowDayOverflow.no);
5830 assert(date == Date(4, 1, 1));
5831 date.roll!"months"(48, AllowDayOverflow.no);
5832 assert(date == Date(4, 1, 1));
5833 }
5834
5835 {
5836 auto date = Date(4, 3, 31);
5837 date.roll!"months"(-49, AllowDayOverflow.no);
5838 assert(date == Date(4, 2, 29));
5839 date.roll!"months"(49, AllowDayOverflow.no);
5840 assert(date == Date(4, 3, 29));
5841 }
5842
5843 {
5844 auto date = Date(4, 3, 31);
5845 date.roll!"months"(-85, AllowDayOverflow.no);
5846 assert(date == Date(4, 2, 29));
5847 date.roll!"months"(85, AllowDayOverflow.no);
5848 assert(date == Date(4, 3, 29));
5849 }
5850
5851 {
5852 auto date = Date(-1, 1, 1);
5853 date.roll!"months"(-1, AllowDayOverflow.no);
5854 assert(date == Date(-1, 12, 1));
5855 date.roll!"months"(1, AllowDayOverflow.no);
5856 assert(date == Date(-1, 1, 1));
5857 }
5858
5859 {
5860 auto date = Date(-4, 1, 1);
5861 date.roll!"months"(-48, AllowDayOverflow.no);
5862 assert(date == Date(-4, 1, 1));
5863 date.roll!"months"(48, AllowDayOverflow.no);
5864 assert(date == Date(-4, 1, 1));
5865 }
5866
5867 {
5868 auto date = Date(-4, 3, 31);
5869 date.roll!"months"(-49, AllowDayOverflow.no);
5870 assert(date == Date(-4, 2, 29));
5871 date.roll!"months"(49, AllowDayOverflow.no);
5872 assert(date == Date(-4, 3, 29));
5873 }
5874
5875 {
5876 auto date = Date(-4, 3, 31);
5877 date.roll!"months"(-85, AllowDayOverflow.no);
5878 assert(date == Date(-4, 2, 29));
5879 date.roll!"months"(85, AllowDayOverflow.no);
5880 assert(date == Date(-4, 3, 29));
5881 }
5882
5883 {
5884 auto date = Date(-3, 3, 31);
5885 date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no);
5886 assert(date == Date(-3, 5, 30));
5887 }
5888 }
5889
5890
5891 /++
5fee5ec3
IB
5892 Adds the given number of units to this $(LREF Date), mutating it. A
5893 negative number will subtract.
b4c522fa
IB
5894
5895 The difference between rolling and adding is that rolling does not
5896 affect larger units. For instance, rolling a $(LREF Date) one
5897 year's worth of days gets the exact same $(LREF Date).
5898
5fee5ec3 5899 The only accepted units are `"days"`.
b4c522fa
IB
5900
5901 Params:
5fee5ec3 5902 units = The units to add. Must be `"days"`.
b4c522fa 5903 days = The number of days to add to this $(LREF Date).
5fee5ec3
IB
5904
5905 Returns:
5906 A reference to the `Date` (`this`).
b4c522fa
IB
5907 +/
5908 ref Date roll(string units)(long days) @safe pure nothrow @nogc
5909 if (units == "days")
5910 {
5911 immutable limit = maxDay(_year, _month);
5912 days %= limit;
5913 auto newDay = _day + days;
5914
5915 if (days < 0)
5916 {
5917 if (newDay < 1)
5918 newDay += limit;
5919 }
5920 else if (newDay > limit)
5921 newDay -= limit;
5922
5923 _day = cast(ubyte) newDay;
5924 return this;
5925 }
5926
5927 ///
5928 @safe unittest
5929 {
5930 auto d = Date(2010, 1, 1);
5931 d.roll!"days"(1);
5932 assert(d == Date(2010, 1, 2));
5933 d.roll!"days"(365);
5934 assert(d == Date(2010, 1, 26));
5935 d.roll!"days"(-32);
5936 assert(d == Date(2010, 1, 25));
5937 }
5938
5939 @safe unittest
5940 {
5941 // Test A.D.
5942 {
5943 auto date = Date(1999, 2, 28);
5944 date.roll!"days"(1);
5945 assert(date == Date(1999, 2, 1));
5946 date.roll!"days"(-1);
5947 assert(date == Date(1999, 2, 28));
5948 }
5949
5950 {
5951 auto date = Date(2000, 2, 28);
5952 date.roll!"days"(1);
5953 assert(date == Date(2000, 2, 29));
5954 date.roll!"days"(1);
5955 assert(date == Date(2000, 2, 1));
5956 date.roll!"days"(-1);
5957 assert(date == Date(2000, 2, 29));
5958 }
5959
5960 {
5961 auto date = Date(1999, 6, 30);
5962 date.roll!"days"(1);
5963 assert(date == Date(1999, 6, 1));
5964 date.roll!"days"(-1);
5965 assert(date == Date(1999, 6, 30));
5966 }
5967
5968 {
5969 auto date = Date(1999, 7, 31);
5970 date.roll!"days"(1);
5971 assert(date == Date(1999, 7, 1));
5972 date.roll!"days"(-1);
5973 assert(date == Date(1999, 7, 31));
5974 }
5975
5976 {
5977 auto date = Date(1999, 1, 1);
5978 date.roll!"days"(-1);
5979 assert(date == Date(1999, 1, 31));
5980 date.roll!"days"(1);
5981 assert(date == Date(1999, 1, 1));
5982 }
5983
5984 {
5985 auto date = Date(1999, 7, 6);
5986 date.roll!"days"(9);
5987 assert(date == Date(1999, 7, 15));
5988 date.roll!"days"(-11);
5989 assert(date == Date(1999, 7, 4));
5990 date.roll!"days"(30);
5991 assert(date == Date(1999, 7, 3));
5992 date.roll!"days"(-3);
5993 assert(date == Date(1999, 7, 31));
5994 }
5995
5996 {
5997 auto date = Date(1999, 7, 6);
5998 date.roll!"days"(365);
5999 assert(date == Date(1999, 7, 30));
6000 date.roll!"days"(-365);
6001 assert(date == Date(1999, 7, 6));
6002 date.roll!"days"(366);
6003 assert(date == Date(1999, 7, 31));
6004 date.roll!"days"(730);
6005 assert(date == Date(1999, 7, 17));
6006 date.roll!"days"(-1096);
6007 assert(date == Date(1999, 7, 6));
6008 }
6009
6010 {
6011 auto date = Date(1999, 2, 6);
6012 date.roll!"days"(365);
6013 assert(date == Date(1999, 2, 7));
6014 date.roll!"days"(-365);
6015 assert(date == Date(1999, 2, 6));
6016 date.roll!"days"(366);
6017 assert(date == Date(1999, 2, 8));
6018 date.roll!"days"(730);
6019 assert(date == Date(1999, 2, 10));
6020 date.roll!"days"(-1096);
6021 assert(date == Date(1999, 2, 6));
6022 }
6023
6024 // Test B.C.
6025 {
6026 auto date = Date(-1999, 2, 28);
6027 date.roll!"days"(1);
6028 assert(date == Date(-1999, 2, 1));
6029 date.roll!"days"(-1);
6030 assert(date == Date(-1999, 2, 28));
6031 }
6032
6033 {
6034 auto date = Date(-2000, 2, 28);
6035 date.roll!"days"(1);
6036 assert(date == Date(-2000, 2, 29));
6037 date.roll!"days"(1);
6038 assert(date == Date(-2000, 2, 1));
6039 date.roll!"days"(-1);
6040 assert(date == Date(-2000, 2, 29));
6041 }
6042
6043 {
6044 auto date = Date(-1999, 6, 30);
6045 date.roll!"days"(1);
6046 assert(date == Date(-1999, 6, 1));
6047 date.roll!"days"(-1);
6048 assert(date == Date(-1999, 6, 30));
6049 }
6050
6051 {
6052 auto date = Date(-1999, 7, 31);
6053 date.roll!"days"(1);
6054 assert(date == Date(-1999, 7, 1));
6055 date.roll!"days"(-1);
6056 assert(date == Date(-1999, 7, 31));
6057 }
6058
6059 {
6060 auto date = Date(-1999, 1, 1);
6061 date.roll!"days"(-1);
6062 assert(date == Date(-1999, 1, 31));
6063 date.roll!"days"(1);
6064 assert(date == Date(-1999, 1, 1));
6065 }
6066
6067 {
6068 auto date = Date(-1999, 7, 6);
6069 date.roll!"days"(9);
6070 assert(date == Date(-1999, 7, 15));
6071 date.roll!"days"(-11);
6072 assert(date == Date(-1999, 7, 4));
6073 date.roll!"days"(30);
6074 assert(date == Date(-1999, 7, 3));
6075 date.roll!"days"(-3);
6076 assert(date == Date(-1999, 7, 31));
6077 }
6078
6079 {
6080 auto date = Date(-1999, 7, 6);
6081 date.roll!"days"(365);
6082 assert(date == Date(-1999, 7, 30));
6083 date.roll!"days"(-365);
6084 assert(date == Date(-1999, 7, 6));
6085 date.roll!"days"(366);
6086 assert(date == Date(-1999, 7, 31));
6087 date.roll!"days"(730);
6088 assert(date == Date(-1999, 7, 17));
6089 date.roll!"days"(-1096);
6090 assert(date == Date(-1999, 7, 6));
6091 }
6092
6093 // Test Both
6094 {
6095 auto date = Date(1, 7, 6);
6096 date.roll!"days"(-365);
6097 assert(date == Date(1, 7, 13));
6098 date.roll!"days"(365);
6099 assert(date == Date(1, 7, 6));
6100 date.roll!"days"(-731);
6101 assert(date == Date(1, 7, 19));
6102 date.roll!"days"(730);
6103 assert(date == Date(1, 7, 5));
6104 }
6105
6106 {
6107 auto date = Date(0, 7, 6);
6108 date.roll!"days"(-365);
6109 assert(date == Date(0, 7, 13));
6110 date.roll!"days"(365);
6111 assert(date == Date(0, 7, 6));
6112 date.roll!"days"(-731);
6113 assert(date == Date(0, 7, 19));
6114 date.roll!"days"(730);
6115 assert(date == Date(0, 7, 5));
6116 }
6117
6118 {
6119 auto date = Date(0, 7, 6);
6120 date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730);
6121 assert(date == Date(0, 7, 8));
6122 }
6123
6124 const cdate = Date(1999, 7, 6);
6125 immutable idate = Date(1999, 7, 6);
6126 static assert(!__traits(compiles, cdate.roll!"days"(12)));
6127 static assert(!__traits(compiles, idate.roll!"days"(12)));
6128 }
6129
5fee5ec3 6130 import core.time : Duration;
b4c522fa
IB
6131 /++
6132 Gives the result of adding or subtracting a $(REF Duration, core,time)
6133 from
6134
6135 The legal types of arithmetic for $(LREF Date) using this operator are
6136
6137 $(BOOKTABLE,
6138 $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6139 $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6140 )
6141
6142 Params:
6143 duration = The $(REF Duration, core,time) to add to or subtract from
6144 this $(LREF Date).
6145 +/
6146 Date opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
6147 if (op == "+" || op == "-")
6148 {
6149 Date retval = this;
6150 immutable days = duration.total!"days";
6151 mixin("return retval._addDays(" ~ op ~ "days);");
6152 }
6153
6154 ///
6155 @safe unittest
6156 {
6157 import core.time : days;
6158
6159 assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
6160 assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
6161
6162 assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
6163 assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
6164 }
6165
6166 @safe unittest
6167 {
6168 auto date = Date(1999, 7, 6);
6169
5fee5ec3 6170 import core.time : dur;
b4c522fa
IB
6171 assert(date + dur!"weeks"(7) == Date(1999, 8, 24));
6172 assert(date + dur!"weeks"(-7) == Date(1999, 5, 18));
6173 assert(date + dur!"days"(7) == Date(1999, 7, 13));
6174 assert(date + dur!"days"(-7) == Date(1999, 6, 29));
6175
6176 assert(date + dur!"hours"(24) == Date(1999, 7, 7));
6177 assert(date + dur!"hours"(-24) == Date(1999, 7, 5));
6178 assert(date + dur!"minutes"(1440) == Date(1999, 7, 7));
6179 assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5));
6180 assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7));
6181 assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6182 assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6183 assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6184 assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6185 assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6186 assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6187 assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6188
6189 assert(date - dur!"weeks"(-7) == Date(1999, 8, 24));
6190 assert(date - dur!"weeks"(7) == Date(1999, 5, 18));
6191 assert(date - dur!"days"(-7) == Date(1999, 7, 13));
6192 assert(date - dur!"days"(7) == Date(1999, 6, 29));
6193
6194 assert(date - dur!"hours"(-24) == Date(1999, 7, 7));
6195 assert(date - dur!"hours"(24) == Date(1999, 7, 5));
6196 assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7));
6197 assert(date - dur!"minutes"(1440) == Date(1999, 7, 5));
6198 assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6199 assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5));
6200 assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6201 assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6202 assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6203 assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6204 assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6205 assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6206
6207 auto duration = dur!"days"(12);
6208 const cdate = Date(1999, 7, 6);
6209 immutable idate = Date(1999, 7, 6);
6210 assert(date + duration == Date(1999, 7, 18));
6211 assert(cdate + duration == Date(1999, 7, 18));
6212 assert(idate + duration == Date(1999, 7, 18));
6213
6214 assert(date - duration == Date(1999, 6, 24));
6215 assert(cdate - duration == Date(1999, 6, 24));
6216 assert(idate - duration == Date(1999, 6, 24));
6217 }
6218
b4c522fa
IB
6219
6220 /++
6221 Gives the result of adding or subtracting a $(REF Duration, core,time)
6222 from this $(LREF Date), as well as assigning the result to this
6223 $(LREF Date).
6224
6225 The legal types of arithmetic for $(LREF Date) using this operator are
6226
6227 $(BOOKTABLE,
6228 $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
6229 $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
6230 )
6231
6232 Params:
6233 duration = The $(REF Duration, core,time) to add to or subtract from
6234 this $(LREF Date).
6235 +/
6236 ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
6237 if (op == "+" || op == "-")
6238 {
6239 immutable days = duration.total!"days";
6240 mixin("return _addDays(" ~ op ~ "days);");
6241 }
6242
6243 @safe unittest
6244 {
5fee5ec3 6245 import core.time : dur;
b4c522fa
IB
6246 assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24));
6247 assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18));
6248 assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13));
6249 assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29));
6250
6251 assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7));
6252 assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5));
6253 assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7));
6254 assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5));
6255 assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7));
6256 assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5));
6257 assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7));
6258 assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5));
6259 assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7));
6260 assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5));
6261 assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7));
6262 assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5));
6263
6264 assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24));
6265 assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18));
6266 assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13));
6267 assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29));
6268
6269 assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7));
6270 assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5));
6271 assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7));
6272 assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5));
6273 assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7));
6274 assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5));
6275 assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7));
6276 assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5));
6277 assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7));
6278 assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5));
6279 assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7));
6280 assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5));
6281
6282 {
6283 auto date = Date(0, 1, 31);
6284 (date += dur!"days"(507)) += dur!"days"(-2);
6285 assert(date == Date(1, 6, 19));
6286 }
6287
6288 auto duration = dur!"days"(12);
6289 auto date = Date(1999, 7, 6);
6290 const cdate = Date(1999, 7, 6);
6291 immutable idate = Date(1999, 7, 6);
6292 date += duration;
6293 static assert(!__traits(compiles, cdate += duration));
6294 static assert(!__traits(compiles, idate += duration));
6295
6296 date -= duration;
6297 static assert(!__traits(compiles, cdate -= duration));
6298 static assert(!__traits(compiles, idate -= duration));
6299 }
6300
b4c522fa
IB
6301
6302 /++
6303 Gives the difference between two $(LREF Date)s.
6304
6305 The legal types of arithmetic for $(LREF Date) using this operator are
6306
6307 $(BOOKTABLE,
6308 $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration))
6309 )
6310 +/
5fee5ec3 6311 Duration opBinary(string op)(Date rhs) const @safe pure nothrow @nogc
b4c522fa
IB
6312 if (op == "-")
6313 {
5fee5ec3 6314 import core.time : dur;
b4c522fa
IB
6315 return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal);
6316 }
6317
6318 @safe unittest
6319 {
6320 auto date = Date(1999, 7, 6);
6321
5fee5ec3 6322 import core.time : dur;
b4c522fa
IB
6323 assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365));
6324 assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365));
6325 assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31));
6326 assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31));
6327 assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1));
6328 assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1));
6329
6330 const cdate = Date(1999, 7, 6);
6331 immutable idate = Date(1999, 7, 6);
6332 assert(date - date == Duration.zero);
6333 assert(cdate - date == Duration.zero);
6334 assert(idate - date == Duration.zero);
6335
6336 assert(date - cdate == Duration.zero);
6337 assert(cdate - cdate == Duration.zero);
6338 assert(idate - cdate == Duration.zero);
6339
6340 assert(date - idate == Duration.zero);
6341 assert(cdate - idate == Duration.zero);
6342 assert(idate - idate == Duration.zero);
6343 }
6344
6345
6346 /++
6347 Returns the difference between the two $(LREF Date)s in months.
6348
6349 To get the difference in years, subtract the year property
6350 of two $(LREF Date)s. To get the difference in days or weeks,
6351 subtract the $(LREF Date)s themselves and use the
6352 $(REF Duration, core,time) that results. Because converting between
6353 months and smaller units requires a specific date (which
6354 $(REF Duration, core,time)s don't have), getting the difference in
6355 months requires some math using both the year and month properties, so
6356 this is a convenience function for getting the difference in months.
6357
6358 Note that the number of days in the months or how far into the month
6359 either $(LREF Date) is is irrelevant. It is the difference in the month
6360 property combined with the difference in years * 12. So, for instance,
6361 December 31st and January 1st are one month apart just as December 1st
6362 and January 31st are one month apart.
6363
6364 Params:
6365 rhs = The $(LREF Date) to subtract from this one.
6366 +/
5fee5ec3 6367 int diffMonths(Date rhs) const @safe pure nothrow @nogc
b4c522fa
IB
6368 {
6369 immutable yearDiff = _year - rhs._year;
6370 immutable monthDiff = _month - rhs._month;
6371
6372 return yearDiff * 12 + monthDiff;
6373 }
6374
6375 ///
6376 @safe unittest
6377 {
6378 assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
6379 assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
6380 assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
6381 assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
6382 }
6383
6384 @safe unittest
6385 {
6386 auto date = Date(1999, 7, 6);
6387
6388 // Test A.D.
6389 assert(date.diffMonths(Date(1998, 6, 5)) == 13);
6390 assert(date.diffMonths(Date(1998, 7, 5)) == 12);
6391 assert(date.diffMonths(Date(1998, 8, 5)) == 11);
6392 assert(date.diffMonths(Date(1998, 9, 5)) == 10);
6393 assert(date.diffMonths(Date(1998, 10, 5)) == 9);
6394 assert(date.diffMonths(Date(1998, 11, 5)) == 8);
6395 assert(date.diffMonths(Date(1998, 12, 5)) == 7);
6396 assert(date.diffMonths(Date(1999, 1, 5)) == 6);
6397 assert(date.diffMonths(Date(1999, 2, 6)) == 5);
6398 assert(date.diffMonths(Date(1999, 3, 6)) == 4);
6399 assert(date.diffMonths(Date(1999, 4, 6)) == 3);
6400 assert(date.diffMonths(Date(1999, 5, 6)) == 2);
6401 assert(date.diffMonths(Date(1999, 6, 6)) == 1);
6402 assert(date.diffMonths(date) == 0);
6403 assert(date.diffMonths(Date(1999, 8, 6)) == -1);
6404 assert(date.diffMonths(Date(1999, 9, 6)) == -2);
6405 assert(date.diffMonths(Date(1999, 10, 6)) == -3);
6406 assert(date.diffMonths(Date(1999, 11, 6)) == -4);
6407 assert(date.diffMonths(Date(1999, 12, 6)) == -5);
6408 assert(date.diffMonths(Date(2000, 1, 6)) == -6);
6409 assert(date.diffMonths(Date(2000, 2, 6)) == -7);
6410 assert(date.diffMonths(Date(2000, 3, 6)) == -8);
6411 assert(date.diffMonths(Date(2000, 4, 6)) == -9);
6412 assert(date.diffMonths(Date(2000, 5, 6)) == -10);
6413 assert(date.diffMonths(Date(2000, 6, 6)) == -11);
6414 assert(date.diffMonths(Date(2000, 7, 6)) == -12);
6415 assert(date.diffMonths(Date(2000, 8, 6)) == -13);
6416
6417 assert(Date(1998, 6, 5).diffMonths(date) == -13);
6418 assert(Date(1998, 7, 5).diffMonths(date) == -12);
6419 assert(Date(1998, 8, 5).diffMonths(date) == -11);
6420 assert(Date(1998, 9, 5).diffMonths(date) == -10);
6421 assert(Date(1998, 10, 5).diffMonths(date) == -9);
6422 assert(Date(1998, 11, 5).diffMonths(date) == -8);
6423 assert(Date(1998, 12, 5).diffMonths(date) == -7);
6424 assert(Date(1999, 1, 5).diffMonths(date) == -6);
6425 assert(Date(1999, 2, 6).diffMonths(date) == -5);
6426 assert(Date(1999, 3, 6).diffMonths(date) == -4);
6427 assert(Date(1999, 4, 6).diffMonths(date) == -3);
6428 assert(Date(1999, 5, 6).diffMonths(date) == -2);
6429 assert(Date(1999, 6, 6).diffMonths(date) == -1);
6430 assert(Date(1999, 8, 6).diffMonths(date) == 1);
6431 assert(Date(1999, 9, 6).diffMonths(date) == 2);
6432 assert(Date(1999, 10, 6).diffMonths(date) == 3);
6433 assert(Date(1999, 11, 6).diffMonths(date) == 4);
6434 assert(Date(1999, 12, 6).diffMonths(date) == 5);
6435 assert(Date(2000, 1, 6).diffMonths(date) == 6);
6436 assert(Date(2000, 2, 6).diffMonths(date) == 7);
6437 assert(Date(2000, 3, 6).diffMonths(date) == 8);
6438 assert(Date(2000, 4, 6).diffMonths(date) == 9);
6439 assert(Date(2000, 5, 6).diffMonths(date) == 10);
6440 assert(Date(2000, 6, 6).diffMonths(date) == 11);
6441 assert(Date(2000, 7, 6).diffMonths(date) == 12);
6442 assert(Date(2000, 8, 6).diffMonths(date) == 13);
6443
6444 assert(date.diffMonths(Date(1999, 6, 30)) == 1);
6445 assert(date.diffMonths(Date(1999, 7, 1)) == 0);
6446 assert(date.diffMonths(Date(1999, 7, 6)) == 0);
6447 assert(date.diffMonths(Date(1999, 7, 11)) == 0);
6448 assert(date.diffMonths(Date(1999, 7, 16)) == 0);
6449 assert(date.diffMonths(Date(1999, 7, 21)) == 0);
6450 assert(date.diffMonths(Date(1999, 7, 26)) == 0);
6451 assert(date.diffMonths(Date(1999, 7, 31)) == 0);
6452 assert(date.diffMonths(Date(1999, 8, 1)) == -1);
6453
6454 assert(date.diffMonths(Date(1990, 6, 30)) == 109);
6455 assert(date.diffMonths(Date(1990, 7, 1)) == 108);
6456 assert(date.diffMonths(Date(1990, 7, 6)) == 108);
6457 assert(date.diffMonths(Date(1990, 7, 11)) == 108);
6458 assert(date.diffMonths(Date(1990, 7, 16)) == 108);
6459 assert(date.diffMonths(Date(1990, 7, 21)) == 108);
6460 assert(date.diffMonths(Date(1990, 7, 26)) == 108);
6461 assert(date.diffMonths(Date(1990, 7, 31)) == 108);
6462 assert(date.diffMonths(Date(1990, 8, 1)) == 107);
6463
6464 assert(Date(1999, 6, 30).diffMonths(date) == -1);
6465 assert(Date(1999, 7, 1).diffMonths(date) == 0);
6466 assert(Date(1999, 7, 6).diffMonths(date) == 0);
6467 assert(Date(1999, 7, 11).diffMonths(date) == 0);
6468 assert(Date(1999, 7, 16).diffMonths(date) == 0);
6469 assert(Date(1999, 7, 21).diffMonths(date) == 0);
6470 assert(Date(1999, 7, 26).diffMonths(date) == 0);
6471 assert(Date(1999, 7, 31).diffMonths(date) == 0);
6472 assert(Date(1999, 8, 1).diffMonths(date) == 1);
6473
6474 assert(Date(1990, 6, 30).diffMonths(date) == -109);
6475 assert(Date(1990, 7, 1).diffMonths(date) == -108);
6476 assert(Date(1990, 7, 6).diffMonths(date) == -108);
6477 assert(Date(1990, 7, 11).diffMonths(date) == -108);
6478 assert(Date(1990, 7, 16).diffMonths(date) == -108);
6479 assert(Date(1990, 7, 21).diffMonths(date) == -108);
6480 assert(Date(1990, 7, 26).diffMonths(date) == -108);
6481 assert(Date(1990, 7, 31).diffMonths(date) == -108);
6482 assert(Date(1990, 8, 1).diffMonths(date) == -107);
6483
6484 // Test B.C.
6485 auto dateBC = Date(-1999, 7, 6);
6486
6487 assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13);
6488 assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12);
6489 assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11);
6490 assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10);
6491 assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9);
6492 assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8);
6493 assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7);
6494 assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6);
6495 assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5);
6496 assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4);
6497 assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3);
6498 assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2);
6499 assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1);
6500 assert(dateBC.diffMonths(dateBC) == 0);
6501 assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1);
6502 assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2);
6503 assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3);
6504 assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4);
6505 assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5);
6506 assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6);
6507 assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7);
6508 assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8);
6509 assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9);
6510 assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10);
6511 assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11);
6512 assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12);
6513 assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13);
6514
6515 assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13);
6516 assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12);
6517 assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11);
6518 assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10);
6519 assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9);
6520 assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8);
6521 assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7);
6522 assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6);
6523 assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5);
6524 assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4);
6525 assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3);
6526 assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2);
6527 assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1);
6528 assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1);
6529 assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2);
6530 assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3);
6531 assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4);
6532 assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5);
6533 assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6);
6534 assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7);
6535 assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8);
6536 assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9);
6537 assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10);
6538 assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11);
6539 assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12);
6540 assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13);
6541
6542 assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1);
6543 assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0);
6544 assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0);
6545 assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0);
6546 assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0);
6547 assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0);
6548 assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0);
6549 assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0);
6550 assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1);
6551
6552 assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109);
6553 assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108);
6554 assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108);
6555 assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108);
6556 assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108);
6557 assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108);
6558 assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108);
6559 assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108);
6560 assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107);
6561
6562 assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1);
6563 assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0);
6564 assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0);
6565 assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0);
6566 assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0);
6567 assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0);
6568 assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0);
6569 assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0);
6570 assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1);
6571
6572 assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109);
6573 assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108);
6574 assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108);
6575 assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108);
6576 assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108);
6577 assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108);
6578 assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108);
6579 assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108);
6580 assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107);
6581
6582 // Test Both
6583 assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94);
6584 assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94);
6585
6586 const cdate = Date(1999, 7, 6);
6587 immutable idate = Date(1999, 7, 6);
6588 assert(date.diffMonths(date) == 0);
6589 assert(cdate.diffMonths(date) == 0);
6590 assert(idate.diffMonths(date) == 0);
6591
6592 assert(date.diffMonths(cdate) == 0);
6593 assert(cdate.diffMonths(cdate) == 0);
6594 assert(idate.diffMonths(cdate) == 0);
6595
6596 assert(date.diffMonths(idate) == 0);
6597 assert(cdate.diffMonths(idate) == 0);
6598 assert(idate.diffMonths(idate) == 0);
6599 }
6600
6601
6602 /++
6603 Whether this $(LREF Date) is in a leap year.
6604 +/
6605 @property bool isLeapYear() const @safe pure nothrow @nogc
6606 {
6607 return yearIsLeapYear(_year);
6608 }
6609
6610 @safe unittest
6611 {
6612 auto date = Date(1999, 7, 6);
6613 const cdate = Date(1999, 7, 6);
6614 immutable idate = Date(1999, 7, 6);
6615 static assert(!__traits(compiles, date.isLeapYear = true));
6616 static assert(!__traits(compiles, cdate.isLeapYear = true));
6617 static assert(!__traits(compiles, idate.isLeapYear = true));
6618 }
6619
6620
6621 /++
6622 Day of the week this $(LREF Date) is on.
6623 +/
6624 @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc
6625 {
6626 return getDayOfWeek(dayOfGregorianCal);
6627 }
6628
6629 @safe unittest
6630 {
6631 const cdate = Date(1999, 7, 6);
6632 immutable idate = Date(1999, 7, 6);
6633 assert(cdate.dayOfWeek == DayOfWeek.tue);
6634 static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun));
6635 assert(idate.dayOfWeek == DayOfWeek.tue);
6636 static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun));
6637 }
6638
6639
6640 /++
6641 Day of the year this $(LREF Date) is on.
6642 +/
6643 @property ushort dayOfYear() const @safe pure nothrow @nogc
6644 {
6645 if (_month >= Month.jan && _month <= Month.dec)
6646 {
6647 immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6648 auto monthIndex = _month - Month.jan;
6649
6650 return cast(ushort)(lastDay[monthIndex] + _day);
6651 }
6652 assert(0, "Invalid month.");
6653 }
6654
6655 ///
6656 @safe unittest
6657 {
6658 assert(Date(1999, 1, 1).dayOfYear == 1);
6659 assert(Date(1999, 12, 31).dayOfYear == 365);
6660 assert(Date(2000, 12, 31).dayOfYear == 366);
6661 }
6662
6663 @safe unittest
6664 {
6665 import std.algorithm.iteration : filter;
6666 import std.range : chain;
6667
6668 foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6669 {
6670 foreach (doy; testDaysOfYear)
6671 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6672 }
6673
6674 foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD)))
6675 {
6676 foreach (doy; testDaysOfLeapYear)
6677 assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day);
6678 }
6679
6680 const cdate = Date(1999, 7, 6);
6681 immutable idate = Date(1999, 7, 6);
6682 assert(cdate.dayOfYear == 187);
6683 assert(idate.dayOfYear == 187);
6684 }
6685
6686 /++
6687 Day of the year.
6688
6689 Params:
6690 day = The day of the year to set which day of the year this
6691 $(LREF Date) is on.
6692
6693 Throws:
6694 $(REF DateTimeException,std,datetime,date) if the given day is an
6695 invalid day of the year.
6696 +/
6697 @property void dayOfYear(int day) @safe pure
6698 {
6699 setDayOfYear!true(day);
6700 }
6701
6702 private void setDayOfYear(bool useExceptions = false)(int day)
6703 {
6704 immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
6705
6706 bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
6707 enum errorMsg = "Invalid day of the year.";
6708
6709 static if (useExceptions)
6710 {
6711 if (dayOutOfRange) throw new DateTimeException(errorMsg);
6712 }
6713 else
6714 {
6715 assert(!dayOutOfRange, errorMsg);
6716 }
6717
6718 foreach (i; 1 .. lastDay.length)
6719 {
6720 if (day <= lastDay[i])
6721 {
6722 _month = cast(Month)(cast(int) Month.jan + i - 1);
6723 _day = cast(ubyte)(day - lastDay[i - 1]);
6724 return;
6725 }
6726 }
6727 assert(0, "Invalid day of the year.");
6728 }
6729
6730 @safe unittest
6731 {
6732 static void test(Date date, int day, MonthDay expected, size_t line = __LINE__)
6733 {
6734 date.dayOfYear = day;
6735 assert(date.month == expected.month);
6736 assert(date.day == expected.day);
6737 }
6738
6739 foreach (doy; testDaysOfYear)
6740 {
6741 test(Date(1999, 1, 1), doy.day, doy.md);
6742 test(Date(-1, 1, 1), doy.day, doy.md);
6743 }
6744
6745 foreach (doy; testDaysOfLeapYear)
6746 {
6747 test(Date(2000, 1, 1), doy.day, doy.md);
6748 test(Date(-4, 1, 1), doy.day, doy.md);
6749 }
6750
6751 const cdate = Date(1999, 7, 6);
6752 immutable idate = Date(1999, 7, 6);
6753 static assert(!__traits(compiles, cdate.dayOfYear = 187));
6754 static assert(!__traits(compiles, idate.dayOfYear = 187));
6755 }
6756
6757
6758 /++
6759 The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6760 +/
6761 @property int dayOfGregorianCal() const @safe pure nothrow @nogc
6762 {
6763 if (isAD)
6764 {
6765 if (_year == 1)
6766 return dayOfYear;
6767
6768 int years = _year - 1;
6769 auto days = (years / 400) * daysIn400Years;
6770 years %= 400;
6771
6772 days += (years / 100) * daysIn100Years;
6773 years %= 100;
6774
6775 days += (years / 4) * daysIn4Years;
6776 years %= 4;
6777
6778 days += years * daysInYear;
6779
6780 days += dayOfYear;
6781
6782 return days;
6783 }
6784 else if (_year == 0)
6785 return dayOfYear - daysInLeapYear;
6786 else
6787 {
6788 int years = _year;
6789 auto days = (years / 400) * daysIn400Years;
6790 years %= 400;
6791
6792 days += (years / 100) * daysIn100Years;
6793 years %= 100;
6794
6795 days += (years / 4) * daysIn4Years;
6796 years %= 4;
6797
6798 if (years < 0)
6799 {
6800 days -= daysInLeapYear;
6801 ++years;
6802
6803 days += years * daysInYear;
6804
6805 days -= daysInYear - dayOfYear;
6806 }
6807 else
6808 days -= daysInLeapYear - dayOfYear;
6809
6810 return days;
6811 }
6812 }
6813
6814 ///
6815 @safe unittest
6816 {
6817 assert(Date(1, 1, 1).dayOfGregorianCal == 1);
6818 assert(Date(1, 12, 31).dayOfGregorianCal == 365);
6819 assert(Date(2, 1, 1).dayOfGregorianCal == 366);
6820
6821 assert(Date(0, 12, 31).dayOfGregorianCal == 0);
6822 assert(Date(0, 1, 1).dayOfGregorianCal == -365);
6823 assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
6824
6825 assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
6826 assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
6827 }
6828
6829 @safe unittest
6830 {
6831 import std.range : chain;
6832
6833 foreach (gd; chain(testGregDaysBC, testGregDaysAD))
6834 assert(gd.date.dayOfGregorianCal == gd.day);
6835
6836 auto date = Date(1999, 7, 6);
6837 const cdate = Date(1999, 7, 6);
6838 immutable idate = Date(1999, 7, 6);
6839 assert(date.dayOfGregorianCal == 729_941);
6840 assert(cdate.dayOfGregorianCal == 729_941);
6841 assert(idate.dayOfGregorianCal == 729_941);
6842 }
6843
6844 /++
6845 The Xth day of the Gregorian Calendar that this $(LREF Date) is on.
6846
6847 Params:
6848 day = The day of the Gregorian Calendar to set this $(LREF Date) to.
6849 +/
6850 @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc
6851 {
6852 this = Date(day);
6853 }
6854
6855 ///
6856 @safe unittest
6857 {
6858 auto date = Date.init;
6859 date.dayOfGregorianCal = 1;
6860 assert(date == Date(1, 1, 1));
6861
6862 date.dayOfGregorianCal = 365;
6863 assert(date == Date(1, 12, 31));
6864
6865 date.dayOfGregorianCal = 366;
6866 assert(date == Date(2, 1, 1));
6867
6868 date.dayOfGregorianCal = 0;
6869 assert(date == Date(0, 12, 31));
6870
6871 date.dayOfGregorianCal = -365;
6872 assert(date == Date(-0, 1, 1));
6873
6874 date.dayOfGregorianCal = -366;
6875 assert(date == Date(-1, 12, 31));
6876
6877 date.dayOfGregorianCal = 730_120;
6878 assert(date == Date(2000, 1, 1));
6879
6880 date.dayOfGregorianCal = 734_137;
6881 assert(date == Date(2010, 12, 31));
6882 }
6883
6884 @safe unittest
6885 {
6886 auto date = Date(1999, 7, 6);
6887 const cdate = Date(1999, 7, 6);
6888 immutable idate = Date(1999, 7, 6);
6889 date.dayOfGregorianCal = 187;
6890 assert(date.dayOfGregorianCal == 187);
6891 static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187));
6892 static assert(!__traits(compiles, idate.dayOfGregorianCal = 187));
6893 }
6894
6895
6896 /++
5fee5ec3
IB
6897 The ISO 8601 week and year of the year that this $(LREF Date) is in.
6898
6899 Returns:
6900 An anonymous struct with the members $(D isoWeekYear) for the
6901 resulting year and $(D isoWeek) for the resulting ISO week.
b4c522fa
IB
6902
6903 See_Also:
6904 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6905 +/
5fee5ec3 6906 @property auto isoWeekAndYear() const @safe pure nothrow
b4c522fa 6907 {
5fee5ec3
IB
6908 struct ISOWeekAndYear { short isoWeekYear; ubyte isoWeek; }
6909
b4c522fa
IB
6910 immutable weekday = dayOfWeek;
6911 immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
6912 immutable week = (dayOfYear - adjustedWeekday + 10) / 7;
6913
6914 try
6915 {
6916 if (week == 53)
6917 {
6918 switch (Date(_year + 1, 1, 1).dayOfWeek)
6919 {
6920 case DayOfWeek.mon:
6921 case DayOfWeek.tue:
6922 case DayOfWeek.wed:
6923 case DayOfWeek.thu:
5fee5ec3 6924 return ISOWeekAndYear(cast(short) (_year + 1), 1);
b4c522fa
IB
6925 case DayOfWeek.fri:
6926 case DayOfWeek.sat:
6927 case DayOfWeek.sun:
5fee5ec3 6928 return ISOWeekAndYear(_year, 53);
b4c522fa
IB
6929 default:
6930 assert(0, "Invalid ISO Week");
6931 }
6932 }
6933 else if (week > 0)
5fee5ec3 6934 return ISOWeekAndYear(_year, cast(ubyte) week);
b4c522fa 6935 else
5fee5ec3 6936 return Date(_year - 1, 12, 31).isoWeekAndYear;
b4c522fa
IB
6937 }
6938 catch (Exception e)
6939 assert(0, "Date's constructor threw.");
6940 }
6941
5fee5ec3
IB
6942 /++
6943 The ISO 8601 week of the year that this $(LREF Date) is in.
6944
6945 See_Also:
6946 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
6947 +/
6948 @property ubyte isoWeek() const @safe pure nothrow
6949 {
6950 return isoWeekAndYear().isoWeek;
6951 }
6952
b4c522fa
IB
6953 @safe unittest
6954 {
6955 // Test A.D.
6956 assert(Date(2009, 12, 28).isoWeek == 53);
6957 assert(Date(2009, 12, 29).isoWeek == 53);
6958 assert(Date(2009, 12, 30).isoWeek == 53);
6959 assert(Date(2009, 12, 31).isoWeek == 53);
6960 assert(Date(2010, 1, 1).isoWeek == 53);
6961 assert(Date(2010, 1, 2).isoWeek == 53);
6962 assert(Date(2010, 1, 3).isoWeek == 53);
6963 assert(Date(2010, 1, 4).isoWeek == 1);
6964 assert(Date(2010, 1, 5).isoWeek == 1);
6965 assert(Date(2010, 1, 6).isoWeek == 1);
6966 assert(Date(2010, 1, 7).isoWeek == 1);
6967 assert(Date(2010, 1, 8).isoWeek == 1);
6968 assert(Date(2010, 1, 9).isoWeek == 1);
6969 assert(Date(2010, 1, 10).isoWeek == 1);
6970 assert(Date(2010, 1, 11).isoWeek == 2);
6971 assert(Date(2010, 12, 31).isoWeek == 52);
6972
6973 assert(Date(2004, 12, 26).isoWeek == 52);
6974 assert(Date(2004, 12, 27).isoWeek == 53);
6975 assert(Date(2004, 12, 28).isoWeek == 53);
6976 assert(Date(2004, 12, 29).isoWeek == 53);
6977 assert(Date(2004, 12, 30).isoWeek == 53);
6978 assert(Date(2004, 12, 31).isoWeek == 53);
6979 assert(Date(2005, 1, 1).isoWeek == 53);
6980 assert(Date(2005, 1, 2).isoWeek == 53);
6981
6982 assert(Date(2005, 12, 31).isoWeek == 52);
6983 assert(Date(2007, 1, 1).isoWeek == 1);
6984
6985 assert(Date(2007, 12, 30).isoWeek == 52);
6986 assert(Date(2007, 12, 31).isoWeek == 1);
6987 assert(Date(2008, 1, 1).isoWeek == 1);
6988
6989 assert(Date(2008, 12, 28).isoWeek == 52);
6990 assert(Date(2008, 12, 29).isoWeek == 1);
6991 assert(Date(2008, 12, 30).isoWeek == 1);
6992 assert(Date(2008, 12, 31).isoWeek == 1);
6993 assert(Date(2009, 1, 1).isoWeek == 1);
6994 assert(Date(2009, 1, 2).isoWeek == 1);
6995 assert(Date(2009, 1, 3).isoWeek == 1);
6996 assert(Date(2009, 1, 4).isoWeek == 1);
6997
6998 // Test B.C.
6999 // The algorithm should work identically for both A.D. and B.C. since
7000 // it doesn't really take the year into account, so B.C. testing
7001 // probably isn't really needed.
7002 assert(Date(0, 12, 31).isoWeek == 52);
7003 assert(Date(0, 1, 4).isoWeek == 1);
7004 assert(Date(0, 1, 1).isoWeek == 52);
7005
7006 const cdate = Date(1999, 7, 6);
7007 immutable idate = Date(1999, 7, 6);
7008 assert(cdate.isoWeek == 27);
7009 static assert(!__traits(compiles, cdate.isoWeek = 3));
7010 assert(idate.isoWeek == 27);
7011 static assert(!__traits(compiles, idate.isoWeek = 3));
7012 }
7013
5fee5ec3
IB
7014 /++
7015 The year inside the ISO 8601 week calendar that this $(LREF Date) is in.
7016
7017 May differ from $(LREF year) between 28 December and 4 January.
7018
7019 See_Also:
7020 $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date)
7021 +/
7022 @property short isoWeekYear() const @safe pure nothrow
7023 {
7024 return isoWeekAndYear().isoWeekYear;
7025 }
7026
7027 @safe unittest
7028 {
7029 // Test A.D.
7030 assert(Date(2009, 12, 28).isoWeekYear == 2009);
7031 assert(Date(2009, 12, 29).isoWeekYear == 2009);
7032 assert(Date(2009, 12, 30).isoWeekYear == 2009);
7033 assert(Date(2009, 12, 31).isoWeekYear == 2009);
7034 assert(Date(2010, 1, 1).isoWeekYear == 2009);
7035 assert(Date(2010, 1, 2).isoWeekYear == 2009);
7036 assert(Date(2010, 1, 3).isoWeekYear == 2009);
7037 assert(Date(2010, 1, 4).isoWeekYear == 2010);
7038 assert(Date(2010, 1, 5).isoWeekYear == 2010);
7039 assert(Date(2010, 1, 6).isoWeekYear == 2010);
7040 assert(Date(2010, 1, 7).isoWeekYear == 2010);
7041 assert(Date(2010, 1, 8).isoWeekYear == 2010);
7042 assert(Date(2010, 1, 9).isoWeekYear == 2010);
7043 assert(Date(2010, 1, 10).isoWeekYear == 2010);
7044 assert(Date(2010, 1, 11).isoWeekYear == 2010);
7045 assert(Date(2010, 12, 31).isoWeekYear == 2010);
7046
7047 assert(Date(2004, 12, 26).isoWeekYear == 2004);
7048 assert(Date(2004, 12, 27).isoWeekYear == 2004);
7049 assert(Date(2004, 12, 28).isoWeekYear == 2004);
7050 assert(Date(2004, 12, 29).isoWeekYear == 2004);
7051 assert(Date(2004, 12, 30).isoWeekYear == 2004);
7052 assert(Date(2004, 12, 31).isoWeekYear == 2004);
7053 assert(Date(2005, 1, 1).isoWeekYear == 2004);
7054 assert(Date(2005, 1, 2).isoWeekYear == 2004);
7055 assert(Date(2005, 1, 3).isoWeekYear == 2005);
7056
7057 assert(Date(2005, 12, 31).isoWeekYear == 2005);
7058 assert(Date(2007, 1, 1).isoWeekYear == 2007);
7059
7060 assert(Date(2007, 12, 30).isoWeekYear == 2007);
7061 assert(Date(2007, 12, 31).isoWeekYear == 2008);
7062 assert(Date(2008, 1, 1).isoWeekYear == 2008);
7063
7064 assert(Date(2008, 12, 28).isoWeekYear == 2008);
7065 assert(Date(2008, 12, 29).isoWeekYear == 2009);
7066 assert(Date(2008, 12, 30).isoWeekYear == 2009);
7067 assert(Date(2008, 12, 31).isoWeekYear == 2009);
7068 assert(Date(2009, 1, 1).isoWeekYear == 2009);
7069 assert(Date(2009, 1, 2).isoWeekYear == 2009);
7070 assert(Date(2009, 1, 3).isoWeekYear == 2009);
7071 assert(Date(2009, 1, 4).isoWeekYear == 2009);
7072
7073 // Test B.C.
7074 assert(Date(0, 12, 31).isoWeekYear == 0);
7075 assert(Date(0, 1, 4).isoWeekYear == 0);
7076 assert(Date(0, 1, 1).isoWeekYear == -1);
7077
7078 const cdate = Date(1999, 7, 6);
7079 immutable idate = Date(1999, 7, 6);
7080 assert(cdate.isoWeekYear == 1999);
7081 assert(idate.isoWeekYear == 1999);
7082 }
7083
7084 static Date fromISOWeek(short isoWeekYear, ubyte isoWeek, DayOfWeek weekday) @safe pure nothrow @nogc
7085 {
7086 immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday;
7087 immutable dayOffset = (isoWeek - 1) * 7 + adjustedWeekday;
7088
7089 Date date;
7090 date._year = isoWeekYear;
7091 date._month = Month.jan;
7092 date._day = 3;
7093 immutable startOfYear = date.dayOfWeek;
7094 return date._addDays(dayOffset - startOfYear);
7095 }
7096
7097 @safe unittest
7098 {
7099 // Test -30000 days to 30000 days for matching construction <-> deconstruction
7100 Date date = Date(1, 1, 1);
7101 date._addDays(-30_000);
7102 foreach (day; 0 .. 60_000)
7103 {
7104 const year = date.isoWeekYear;
7105 const dow = date.dayOfWeek;
7106 const isoWeek = date.isoWeek;
7107 const reversed = Date.fromISOWeek(year, isoWeek, dow);
7108 assert(reversed == date, date.toISOExtString ~ " != " ~ reversed.toISOExtString);
7109 date = date._addDays(1);
7110 }
7111 }
7112
b4c522fa
IB
7113
7114 /++
7115 $(LREF Date) for the last day in the month that this $(LREF Date) is in.
7116 +/
7117 @property Date endOfMonth() const @safe pure nothrow
7118 {
7119 try
7120 return Date(_year, _month, maxDay(_year, _month));
7121 catch (Exception e)
7122 assert(0, "Date's constructor threw.");
7123 }
7124
7125 ///
7126 @safe unittest
7127 {
7128 assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
7129 assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
7130 assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
7131 assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
7132 }
7133
7134 @safe unittest
7135 {
7136 // Test A.D.
7137 assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31));
7138 assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28));
7139 assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29));
7140 assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31));
7141 assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30));
7142 assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31));
7143 assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30));
7144 assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31));
7145 assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31));
7146 assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30));
7147 assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31));
7148 assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30));
7149 assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31));
7150
7151 // Test B.C.
7152 assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31));
7153 assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28));
7154 assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29));
7155 assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31));
7156 assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30));
7157 assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31));
7158 assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30));
7159 assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31));
7160 assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31));
7161 assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30));
7162 assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31));
7163 assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30));
7164 assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31));
7165
7166 const cdate = Date(1999, 7, 6);
7167 immutable idate = Date(1999, 7, 6);
7168 static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30)));
7169 static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30)));
7170 }
7171
7172
7173 /++
7174 The last day in the month that this $(LREF Date) is in.
7175 +/
7176 @property ubyte daysInMonth() const @safe pure nothrow @nogc
7177 {
7178 return maxDay(_year, _month);
7179 }
7180
7181 ///
7182 @safe unittest
7183 {
7184 assert(Date(1999, 1, 6).daysInMonth == 31);
7185 assert(Date(1999, 2, 7).daysInMonth == 28);
7186 assert(Date(2000, 2, 7).daysInMonth == 29);
7187 assert(Date(2000, 6, 4).daysInMonth == 30);
7188 }
7189
7190 @safe unittest
7191 {
7192 // Test A.D.
7193 assert(Date(1999, 1, 1).daysInMonth == 31);
7194 assert(Date(1999, 2, 1).daysInMonth == 28);
7195 assert(Date(2000, 2, 1).daysInMonth == 29);
7196 assert(Date(1999, 3, 1).daysInMonth == 31);
7197 assert(Date(1999, 4, 1).daysInMonth == 30);
7198 assert(Date(1999, 5, 1).daysInMonth == 31);
7199 assert(Date(1999, 6, 1).daysInMonth == 30);
7200 assert(Date(1999, 7, 1).daysInMonth == 31);
7201 assert(Date(1999, 8, 1).daysInMonth == 31);
7202 assert(Date(1999, 9, 1).daysInMonth == 30);
7203 assert(Date(1999, 10, 1).daysInMonth == 31);
7204 assert(Date(1999, 11, 1).daysInMonth == 30);
7205 assert(Date(1999, 12, 1).daysInMonth == 31);
7206
7207 // Test B.C.
7208 assert(Date(-1999, 1, 1).daysInMonth == 31);
7209 assert(Date(-1999, 2, 1).daysInMonth == 28);
7210 assert(Date(-2000, 2, 1).daysInMonth == 29);
7211 assert(Date(-1999, 3, 1).daysInMonth == 31);
7212 assert(Date(-1999, 4, 1).daysInMonth == 30);
7213 assert(Date(-1999, 5, 1).daysInMonth == 31);
7214 assert(Date(-1999, 6, 1).daysInMonth == 30);
7215 assert(Date(-1999, 7, 1).daysInMonth == 31);
7216 assert(Date(-1999, 8, 1).daysInMonth == 31);
7217 assert(Date(-1999, 9, 1).daysInMonth == 30);
7218 assert(Date(-1999, 10, 1).daysInMonth == 31);
7219 assert(Date(-1999, 11, 1).daysInMonth == 30);
7220 assert(Date(-1999, 12, 1).daysInMonth == 31);
7221
7222 const cdate = Date(1999, 7, 6);
7223 immutable idate = Date(1999, 7, 6);
7224 static assert(!__traits(compiles, cdate.daysInMonth = 30));
7225 static assert(!__traits(compiles, idate.daysInMonth = 30));
7226 }
7227
7228
7229 /++
7230 Whether the current year is a date in A.D.
7231 +/
7232 @property bool isAD() const @safe pure nothrow @nogc
7233 {
7234 return _year > 0;
7235 }
7236
7237 ///
7238 @safe unittest
7239 {
7240 assert(Date(1, 1, 1).isAD);
7241 assert(Date(2010, 12, 31).isAD);
7242 assert(!Date(0, 12, 31).isAD);
7243 assert(!Date(-2010, 1, 1).isAD);
7244 }
7245
7246 @safe unittest
7247 {
7248 assert(Date(2010, 7, 4).isAD);
7249 assert(Date(1, 1, 1).isAD);
7250 assert(!Date(0, 1, 1).isAD);
7251 assert(!Date(-1, 1, 1).isAD);
7252 assert(!Date(-2010, 7, 4).isAD);
7253
7254 const cdate = Date(1999, 7, 6);
7255 immutable idate = Date(1999, 7, 6);
7256 assert(cdate.isAD);
7257 assert(idate.isAD);
7258 }
7259
7260
7261 /++
7262 The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this
7263 $(LREF Date) at noon (since the Julian day changes at noon).
7264 +/
7265 @property long julianDay() const @safe pure nothrow @nogc
7266 {
7267 return dayOfGregorianCal + 1_721_425;
7268 }
7269
7270 @safe unittest
7271 {
7272 assert(Date(-4713, 11, 24).julianDay == 0);
7273 assert(Date(0, 12, 31).julianDay == 1_721_425);
7274 assert(Date(1, 1, 1).julianDay == 1_721_426);
7275 assert(Date(1582, 10, 15).julianDay == 2_299_161);
7276 assert(Date(1858, 11, 17).julianDay == 2_400_001);
7277 assert(Date(1982, 1, 4).julianDay == 2_444_974);
7278 assert(Date(1996, 3, 31).julianDay == 2_450_174);
7279 assert(Date(2010, 8, 24).julianDay == 2_455_433);
7280
7281 const cdate = Date(1999, 7, 6);
7282 immutable idate = Date(1999, 7, 6);
7283 assert(cdate.julianDay == 2_451_366);
7284 assert(idate.julianDay == 2_451_366);
7285 }
7286
7287
7288 /++
7289 The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for
7290 any time on this date (since, the modified Julian day changes at
7291 midnight).
7292 +/
7293 @property long modJulianDay() const @safe pure nothrow @nogc
7294 {
7295 return julianDay - 2_400_001;
7296 }
7297
7298 @safe unittest
7299 {
7300 assert(Date(1858, 11, 17).modJulianDay == 0);
7301 assert(Date(2010, 8, 24).modJulianDay == 55_432);
7302
7303 const cdate = Date(1999, 7, 6);
7304 immutable idate = Date(1999, 7, 6);
7305 assert(cdate.modJulianDay == 51_365);
7306 assert(idate.modJulianDay == 51_365);
7307 }
7308
7309
7310 /++
5fee5ec3
IB
7311 Converts this $(LREF Date) to a string with the format `YYYYMMDD`.
7312 If `writer` is set, the resulting string will be written directly
7313 to it.
7314
7315 Params:
7316 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7317 Returns:
7318 A `string` when not using an output range; `void` otherwise.
b4c522fa
IB
7319 +/
7320 string toISOString() const @safe pure nothrow
7321 {
5fee5ec3
IB
7322 import std.array : appender;
7323 auto w = appender!string();
7324 w.reserve(8);
b4c522fa 7325 try
5fee5ec3 7326 toISOString(w);
b4c522fa 7327 catch (Exception e)
5fee5ec3
IB
7328 assert(0, "toISOString() threw.");
7329 return w.data;
b4c522fa
IB
7330 }
7331
7332 ///
7333 @safe unittest
7334 {
7335 assert(Date(2010, 7, 4).toISOString() == "20100704");
7336 assert(Date(1998, 12, 25).toISOString() == "19981225");
7337 assert(Date(0, 1, 5).toISOString() == "00000105");
7338 assert(Date(-4, 1, 5).toISOString() == "-00040105");
7339 }
7340
7341 @safe unittest
7342 {
7343 // Test A.D.
7344 assert(Date(9, 12, 4).toISOString() == "00091204");
7345 assert(Date(99, 12, 4).toISOString() == "00991204");
7346 assert(Date(999, 12, 4).toISOString() == "09991204");
7347 assert(Date(9999, 7, 4).toISOString() == "99990704");
7348 assert(Date(10000, 10, 20).toISOString() == "+100001020");
7349
7350 // Test B.C.
7351 assert(Date(0, 12, 4).toISOString() == "00001204");
7352 assert(Date(-9, 12, 4).toISOString() == "-00091204");
7353 assert(Date(-99, 12, 4).toISOString() == "-00991204");
7354 assert(Date(-999, 12, 4).toISOString() == "-09991204");
7355 assert(Date(-9999, 7, 4).toISOString() == "-99990704");
7356 assert(Date(-10000, 10, 20).toISOString() == "-100001020");
7357
7358 const cdate = Date(1999, 7, 6);
7359 immutable idate = Date(1999, 7, 6);
7360 assert(cdate.toISOString() == "19990706");
7361 assert(idate.toISOString() == "19990706");
7362 }
7363
5fee5ec3
IB
7364 /// ditto
7365 void toISOString(W)(ref W writer) const
7366 if (isOutputRange!(W, char))
7367 {
7368 import std.format.write : formattedWrite;
7369 if (_year >= 0)
7370 {
7371 if (_year < 10_000)
7372 formattedWrite(writer, "%04d%02d%02d", _year, _month, _day);
7373 else
7374 formattedWrite(writer, "+%05d%02d%02d", _year, _month, _day);
7375 }
7376 else if (_year > -10_000)
7377 formattedWrite(writer, "%05d%02d%02d", _year, _month, _day);
7378 else
7379 formattedWrite(writer, "%06d%02d%02d", _year, _month, _day);
7380 }
7381
7382 @safe pure unittest
7383 {
7384 import std.array : appender;
7385
7386 auto w = appender!(char[])();
7387 Date(2010, 7, 4).toISOString(w);
7388 assert(w.data == "20100704");
7389 w.clear();
7390 Date(1998, 12, 25).toISOString(w);
7391 assert(w.data == "19981225");
7392 }
7393
b4c522fa 7394 /++
5fee5ec3
IB
7395 Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`.
7396 If `writer` is set, the resulting string will be written directly
7397 to it.
7398
7399 Params:
7400 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7401 Returns:
7402 A `string` when not using an output range; `void` otherwise.
b4c522fa
IB
7403 +/
7404 string toISOExtString() const @safe pure nothrow
7405 {
5fee5ec3
IB
7406 import std.array : appender;
7407 auto w = appender!string();
7408 w.reserve(10);
b4c522fa 7409 try
5fee5ec3 7410 toISOExtString(w);
b4c522fa 7411 catch (Exception e)
5fee5ec3
IB
7412 assert(0, "toISOExtString() threw.");
7413 return w.data;
b4c522fa
IB
7414 }
7415
7416 ///
7417 @safe unittest
7418 {
7419 assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
7420 assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
7421 assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
7422 assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
7423 }
7424
7425 @safe unittest
7426 {
7427 // Test A.D.
7428 assert(Date(9, 12, 4).toISOExtString() == "0009-12-04");
7429 assert(Date(99, 12, 4).toISOExtString() == "0099-12-04");
7430 assert(Date(999, 12, 4).toISOExtString() == "0999-12-04");
7431 assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04");
7432 assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20");
7433
7434 // Test B.C.
7435 assert(Date(0, 12, 4).toISOExtString() == "0000-12-04");
7436 assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04");
7437 assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04");
7438 assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04");
7439 assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04");
7440 assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20");
7441
7442 const cdate = Date(1999, 7, 6);
7443 immutable idate = Date(1999, 7, 6);
7444 assert(cdate.toISOExtString() == "1999-07-06");
7445 assert(idate.toISOExtString() == "1999-07-06");
7446 }
7447
5fee5ec3
IB
7448 /// ditto
7449 void toISOExtString(W)(ref W writer) const
7450 if (isOutputRange!(W, char))
7451 {
7452 import std.format.write : formattedWrite;
7453 if (_year >= 0)
7454 {
7455 if (_year < 10_000)
7456 formattedWrite(writer, "%04d-%02d-%02d", _year, _month, _day);
7457 else
7458 formattedWrite(writer, "+%05d-%02d-%02d", _year, _month, _day);
7459 }
7460 else if (_year > -10_000)
7461 formattedWrite(writer, "%05d-%02d-%02d", _year, _month, _day);
7462 else
7463 formattedWrite(writer, "%06d-%02d-%02d", _year, _month, _day);
7464 }
7465
7466 @safe pure unittest
7467 {
7468 import std.array : appender;
7469
7470 auto w = appender!(char[])();
7471 Date(2010, 7, 4).toISOExtString(w);
7472 assert(w.data == "2010-07-04");
7473 w.clear();
7474 Date(-4, 1, 5).toISOExtString(w);
7475 assert(w.data == "-0004-01-05");
7476 }
7477
b4c522fa 7478 /++
5fee5ec3
IB
7479 Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`.
7480 If `writer` is set, the resulting string will be written directly
7481 to it.
7482
7483 Params:
7484 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
7485 Returns:
7486 A `string` when not using an output range; `void` otherwise.
b4c522fa
IB
7487 +/
7488 string toSimpleString() const @safe pure nothrow
7489 {
5fee5ec3
IB
7490 import std.array : appender;
7491 auto w = appender!string();
7492 w.reserve(11);
b4c522fa 7493 try
5fee5ec3 7494 toSimpleString(w);
b4c522fa 7495 catch (Exception e)
5fee5ec3
IB
7496 assert(0, "toSimpleString() threw.");
7497 return w.data;
b4c522fa
IB
7498 }
7499
7500 ///
7501 @safe unittest
7502 {
7503 assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
7504 assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
7505 assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
7506 assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
7507 }
7508
7509 @safe unittest
7510 {
7511 // Test A.D.
7512 assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04");
7513 assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04");
7514 assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04");
7515 assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04");
7516 assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20");
7517
7518 // Test B.C.
7519 assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04");
7520 assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04");
7521 assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04");
7522 assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04");
7523 assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04");
7524 assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20");
7525
7526 const cdate = Date(1999, 7, 6);
7527 immutable idate = Date(1999, 7, 6);
7528 assert(cdate.toSimpleString() == "1999-Jul-06");
7529 assert(idate.toSimpleString() == "1999-Jul-06");
7530 }
7531
5fee5ec3
IB
7532 /// ditto
7533 void toSimpleString(W)(ref W writer) const
7534 if (isOutputRange!(W, char))
7535 {
7536 import std.format.write : formattedWrite;
7537 if (_year >= 0)
7538 {
7539 if (_year < 10_000)
7540 formattedWrite(writer, "%04d-%s-%02d", _year, monthToString(_month), _day);
7541 else
7542 formattedWrite(writer, "+%05d-%s-%02d", _year, monthToString(_month), _day);
7543 }
7544 else if (_year > -10_000)
7545 formattedWrite(writer, "%05d-%s-%02d", _year, monthToString(_month), _day);
7546 else
7547 formattedWrite(writer, "%06d-%s-%02d", _year, monthToString(_month), _day);
7548 }
7549
7550 @safe pure unittest
7551 {
7552 import std.array : appender;
7553
7554 auto w = appender!(char[])();
7555 Date(9, 12, 4).toSimpleString(w);
7556 assert(w.data == "0009-Dec-04");
7557 w.clear();
7558 Date(-10000, 10, 20).toSimpleString(w);
7559 assert(w.data == "-10000-Oct-20");
7560 }
b4c522fa
IB
7561
7562 /++
7563 Converts this $(LREF Date) to a string.
7564
7565 This function exists to make it easy to convert a $(LREF Date) to a
7566 string for code that does not care what the exact format is - just that
7567 it presents the information in a clear manner. It also makes it easy to
7568 simply convert a $(LREF Date) to a string when using functions such as
7569 `to!string`, `format`, or `writeln` which use toString to convert
7570 user-defined types. So, it is unlikely that much code will call
7571 toString directly.
7572
7573 The format of the string is purposefully unspecified, and code that
7574 cares about the format of the string should use `toISOString`,
7575 `toISOExtString`, `toSimpleString`, or some other custom formatting
7576 function that explicitly generates the format that the code needs. The
7577 reason is that the code is then clear about what format it's using,
7578 making it less error-prone to maintain the code and interact with other
7579 software that consumes the generated strings. It's for this same reason
7580 $(LREF Date) has no `fromString` function, whereas it does have
7581 `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
7582
7583 The format returned by toString may or may not change in the future.
7584 +/
7585 string toString() const @safe pure nothrow
7586 {
7587 return toSimpleString();
7588 }
7589
7590 @safe unittest
7591 {
7592 auto date = Date(1999, 7, 6);
7593 const cdate = Date(1999, 7, 6);
7594 immutable idate = Date(1999, 7, 6);
7595 assert(date.toString());
7596 assert(cdate.toString());
7597 assert(idate.toString());
7598 }
7599
5fee5ec3
IB
7600 /// ditto
7601 void toString(W)(ref W writer) const
7602 if (isOutputRange!(W, char))
7603 {
7604 toSimpleString(writer);
7605 }
b4c522fa
IB
7606
7607 /++
7608 Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace
7609 is stripped from the given string.
7610
7611 Params:
7612 isoString = A string formatted in the ISO format for dates.
7613
7614 Throws:
7615 $(REF DateTimeException,std,datetime,date) if the given string is
7616 not in the ISO format or if the resulting $(LREF Date) would not be
7617 valid.
7618 +/
5fee5ec3 7619 static Date fromISOString(S)(scope const S isoString) @safe pure
b4c522fa
IB
7620 if (isSomeString!S)
7621 {
7622 import std.algorithm.searching : startsWith;
7623 import std.conv : to, text, ConvException;
7624 import std.exception : enforce;
7625 import std.string : strip;
7626
7627 auto str = isoString.strip;
7628
7629 enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString));
7630
7631 int day, month, year;
7632 auto yearStr = str[0 .. $ - 4];
7633
7634 try
7635 {
7636 // using conversion to uint plus cast because it checks for +/-
7637 // for us quickly while throwing ConvException
7638 day = cast(int) to!uint(str[$ - 2 .. $]);
7639 month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
7640
7641 if (yearStr.length > 4)
7642 {
7643 enforce!DateTimeException(yearStr.startsWith('-', '+'),
7644 text("Invalid ISO String: ", isoString));
7645 year = to!int(yearStr);
7646 }
7647 else
7648 {
7649 year = cast(int) to!uint(yearStr);
7650 }
7651 }
7652 catch (ConvException)
7653 {
7654 throw new DateTimeException(text("Invalid ISO String: ", isoString));
7655 }
7656
7657 return Date(year, month, day);
7658 }
7659
7660 ///
7661 @safe unittest
7662 {
7663 assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
7664 assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
7665 assert(Date.fromISOString("00000105") == Date(0, 1, 5));
7666 assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
7667 assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
7668 }
7669
7670 @safe unittest
7671 {
7672 assertThrown!DateTimeException(Date.fromISOString(""));
7673 assertThrown!DateTimeException(Date.fromISOString("990704"));
7674 assertThrown!DateTimeException(Date.fromISOString("0100704"));
7675 assertThrown!DateTimeException(Date.fromISOString("2010070"));
7676 assertThrown!DateTimeException(Date.fromISOString("2010070 "));
7677 assertThrown!DateTimeException(Date.fromISOString("120100704"));
7678 assertThrown!DateTimeException(Date.fromISOString("-0100704"));
7679 assertThrown!DateTimeException(Date.fromISOString("+0100704"));
7680 assertThrown!DateTimeException(Date.fromISOString("2010070a"));
7681 assertThrown!DateTimeException(Date.fromISOString("20100a04"));
7682 assertThrown!DateTimeException(Date.fromISOString("2010a704"));
7683
7684 assertThrown!DateTimeException(Date.fromISOString("99-07-04"));
7685 assertThrown!DateTimeException(Date.fromISOString("010-07-04"));
7686 assertThrown!DateTimeException(Date.fromISOString("2010-07-0"));
7687 assertThrown!DateTimeException(Date.fromISOString("2010-07-0 "));
7688 assertThrown!DateTimeException(Date.fromISOString("12010-07-04"));
7689 assertThrown!DateTimeException(Date.fromISOString("-010-07-04"));
7690 assertThrown!DateTimeException(Date.fromISOString("+010-07-04"));
7691 assertThrown!DateTimeException(Date.fromISOString("2010-07-0a"));
7692 assertThrown!DateTimeException(Date.fromISOString("2010-0a-04"));
7693 assertThrown!DateTimeException(Date.fromISOString("2010-a7-04"));
7694 assertThrown!DateTimeException(Date.fromISOString("2010/07/04"));
7695 assertThrown!DateTimeException(Date.fromISOString("2010/7/04"));
7696 assertThrown!DateTimeException(Date.fromISOString("2010/7/4"));
7697 assertThrown!DateTimeException(Date.fromISOString("2010/07/4"));
7698 assertThrown!DateTimeException(Date.fromISOString("2010-7-04"));
7699 assertThrown!DateTimeException(Date.fromISOString("2010-7-4"));
7700 assertThrown!DateTimeException(Date.fromISOString("2010-07-4"));
7701
7702 assertThrown!DateTimeException(Date.fromISOString("99Jul04"));
7703 assertThrown!DateTimeException(Date.fromISOString("010Jul04"));
7704 assertThrown!DateTimeException(Date.fromISOString("2010Jul0"));
7705 assertThrown!DateTimeException(Date.fromISOString("2010Jul0 "));
7706 assertThrown!DateTimeException(Date.fromISOString("12010Jul04"));
7707 assertThrown!DateTimeException(Date.fromISOString("-010Jul04"));
7708 assertThrown!DateTimeException(Date.fromISOString("+010Jul04"));
7709 assertThrown!DateTimeException(Date.fromISOString("2010Jul0a"));
7710 assertThrown!DateTimeException(Date.fromISOString("2010Jua04"));
7711 assertThrown!DateTimeException(Date.fromISOString("2010aul04"));
7712
7713 assertThrown!DateTimeException(Date.fromISOString("99-Jul-04"));
7714 assertThrown!DateTimeException(Date.fromISOString("010-Jul-04"));
7715 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0"));
7716 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 "));
7717 assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04"));
7718 assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04"));
7719 assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04"));
7720 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a"));
7721 assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04"));
7722 assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04"));
7723 assertThrown!DateTimeException(Date.fromISOString("2010-aul-04"));
7724
7725 assertThrown!DateTimeException(Date.fromISOString("2010-07-04"));
7726 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04"));
7727
7728 assert(Date.fromISOString("19990706") == Date(1999, 7, 6));
7729 assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6));
7730 assert(Date.fromISOString("+019990706") == Date(1999, 7, 6));
7731 assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6));
7732 assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6));
7733 assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6));
7734 }
7735
5fee5ec3 7736 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
7737 @safe unittest
7738 {
7739 import std.conv : to;
7740 import std.meta : AliasSeq;
5fee5ec3 7741 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 7742 {
5fee5ec3 7743 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
7744 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
7745 }
7746 }
7747
7748
7749 /++
7750 Creates a $(LREF Date) from a string with the format YYYY-MM-DD.
7751 Whitespace is stripped from the given string.
7752
7753 Params:
7754 isoExtString = A string formatted in the ISO Extended format for
7755 dates.
7756
7757 Throws:
7758 $(REF DateTimeException,std,datetime,date) if the given string is
7759 not in the ISO Extended format or if the resulting $(LREF Date)
7760 would not be valid.
7761 +/
5fee5ec3 7762 static Date fromISOExtString(S)(scope const S isoExtString) @safe pure
b4c522fa
IB
7763 if (isSomeString!(S))
7764 {
5fee5ec3
IB
7765 import std.algorithm.searching : startsWith;
7766 import std.conv : to, ConvException;
b4c522fa
IB
7767 import std.format : format;
7768 import std.string : strip;
7769
5fee5ec3
IB
7770 auto str = strip(isoExtString);
7771 short year;
7772 ubyte month, day;
b4c522fa 7773
5fee5ec3
IB
7774 if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-')
7775 throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
b4c522fa 7776
5fee5ec3
IB
7777 auto yearStr = str[0 .. $-6];
7778 auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7779 if ((yearStr.length > 4) != signAtBegining)
7780 {
7781 throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
7782 }
b4c522fa 7783
5fee5ec3 7784 try
b4c522fa 7785 {
5fee5ec3
IB
7786 day = to!ubyte(str[$-2 .. $]);
7787 month = to!ubyte(str[$-5 .. $-3]);
7788 year = to!short(yearStr);
7789 }
7790 catch (ConvException)
7791 {
7792 throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString));
b4c522fa 7793 }
b4c522fa 7794
5fee5ec3 7795 return Date(year, month, day);
b4c522fa
IB
7796 }
7797
7798 ///
7799 @safe unittest
7800 {
7801 assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
7802 assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
7803 assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
7804 assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
7805 assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
7806 }
7807
7808 @safe unittest
7809 {
7810 assertThrown!DateTimeException(Date.fromISOExtString(""));
7811 assertThrown!DateTimeException(Date.fromISOExtString("990704"));
7812 assertThrown!DateTimeException(Date.fromISOExtString("0100704"));
7813 assertThrown!DateTimeException(Date.fromISOExtString("2010070"));
7814 assertThrown!DateTimeException(Date.fromISOExtString("2010070 "));
7815 assertThrown!DateTimeException(Date.fromISOExtString("120100704"));
7816 assertThrown!DateTimeException(Date.fromISOExtString("-0100704"));
7817 assertThrown!DateTimeException(Date.fromISOExtString("+0100704"));
7818 assertThrown!DateTimeException(Date.fromISOExtString("2010070a"));
7819 assertThrown!DateTimeException(Date.fromISOExtString("20100a04"));
7820 assertThrown!DateTimeException(Date.fromISOExtString("2010a704"));
7821
7822 assertThrown!DateTimeException(Date.fromISOExtString("99-07-04"));
7823 assertThrown!DateTimeException(Date.fromISOExtString("010-07-04"));
7824 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0"));
7825 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 "));
7826 assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04"));
7827 assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04"));
7828 assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04"));
7829 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a"));
7830 assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04"));
7831 assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04"));
7832 assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04"));
7833 assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04"));
7834 assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4"));
7835 assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4"));
7836 assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04"));
7837 assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4"));
7838 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4"));
7839
7840 assertThrown!DateTimeException(Date.fromISOExtString("99Jul04"));
7841 assertThrown!DateTimeException(Date.fromISOExtString("010Jul04"));
7842 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0"));
7843 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 "));
7844 assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04"));
7845 assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04"));
7846 assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04"));
7847 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a"));
7848 assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04"));
7849 assertThrown!DateTimeException(Date.fromISOExtString("2010aul04"));
7850
7851 assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04"));
7852 assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04"));
7853 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0"));
7854 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 "));
7855 assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04"));
7856 assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04"));
7857 assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04"));
7858 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a"));
7859 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04"));
7860 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04"));
7861 assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04"));
7862
7863 assertThrown!DateTimeException(Date.fromISOExtString("20100704"));
7864 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04"));
7865
7866 assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6));
7867 assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6));
7868 assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6));
7869 assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6));
7870 assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6));
7871 assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6));
7872 }
7873
5fee5ec3 7874 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
7875 @safe unittest
7876 {
7877 import std.conv : to;
7878 import std.meta : AliasSeq;
5fee5ec3 7879 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 7880 {
5fee5ec3 7881 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
7882 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
7883 }
7884 }
7885
7886
7887 /++
7888 Creates a $(LREF Date) from a string with the format YYYY-Mon-DD.
7889 Whitespace is stripped from the given string.
7890
7891 Params:
7892 simpleString = A string formatted in the way that toSimpleString
7893 formats dates.
7894
7895 Throws:
7896 $(REF DateTimeException,std,datetime,date) if the given string is
7897 not in the correct format or if the resulting $(LREF Date) would not
7898 be valid.
7899 +/
5fee5ec3 7900 static Date fromSimpleString(S)(scope const S simpleString) @safe pure
b4c522fa
IB
7901 if (isSomeString!(S))
7902 {
5fee5ec3
IB
7903 import std.algorithm.searching : startsWith;
7904 import std.conv : to, ConvException;
b4c522fa
IB
7905 import std.format : format;
7906 import std.string : strip;
7907
5fee5ec3 7908 auto str = strip(simpleString);
b4c522fa 7909
5fee5ec3
IB
7910 if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-')
7911 throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
b4c522fa 7912
5fee5ec3
IB
7913 int year;
7914 uint day;
7915 auto month = monthFromString(str[$ - 6 .. $ - 3]);
7916 auto yearStr = str[0 .. $ - 7];
7917 auto signAtBegining = cast(bool) yearStr.startsWith('-', '+');
7918 if ((yearStr.length > 4) != signAtBegining)
7919 {
7920 throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
7921 }
b4c522fa 7922
5fee5ec3 7923 try
b4c522fa 7924 {
5fee5ec3
IB
7925 day = to!uint(str[$ - 2 .. $]);
7926 year = to!int(yearStr);
7927 }
7928 catch (ConvException)
7929 {
7930 throw new DateTimeException(format!"Invalid string format: %s"(simpleString));
b4c522fa 7931 }
b4c522fa 7932
5fee5ec3 7933 return Date(year, month, day);
b4c522fa
IB
7934 }
7935
7936 ///
7937 @safe unittest
7938 {
7939 assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
7940 assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
7941 assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
7942 assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
7943 assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
7944 }
7945
7946 @safe unittest
7947 {
7948 assertThrown!DateTimeException(Date.fromSimpleString(""));
7949 assertThrown!DateTimeException(Date.fromSimpleString("990704"));
7950 assertThrown!DateTimeException(Date.fromSimpleString("0100704"));
7951 assertThrown!DateTimeException(Date.fromSimpleString("2010070"));
7952 assertThrown!DateTimeException(Date.fromSimpleString("2010070 "));
7953 assertThrown!DateTimeException(Date.fromSimpleString("120100704"));
7954 assertThrown!DateTimeException(Date.fromSimpleString("-0100704"));
7955 assertThrown!DateTimeException(Date.fromSimpleString("+0100704"));
7956 assertThrown!DateTimeException(Date.fromSimpleString("2010070a"));
7957 assertThrown!DateTimeException(Date.fromSimpleString("20100a04"));
7958 assertThrown!DateTimeException(Date.fromSimpleString("2010a704"));
7959
7960 assertThrown!DateTimeException(Date.fromSimpleString("99-07-04"));
7961 assertThrown!DateTimeException(Date.fromSimpleString("010-07-04"));
7962 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0"));
7963 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 "));
7964 assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04"));
7965 assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04"));
7966 assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04"));
7967 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a"));
7968 assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04"));
7969 assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04"));
7970 assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04"));
7971 assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04"));
7972 assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4"));
7973 assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4"));
7974 assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04"));
7975 assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4"));
7976 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4"));
7977
7978 assertThrown!DateTimeException(Date.fromSimpleString("99Jul04"));
7979 assertThrown!DateTimeException(Date.fromSimpleString("010Jul04"));
7980 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0"));
7981 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 "));
7982 assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04"));
7983 assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04"));
7984 assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04"));
7985 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a"));
7986 assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04"));
7987 assertThrown!DateTimeException(Date.fromSimpleString("2010aul04"));
7988
7989 assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04"));
7990 assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04"));
7991 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0"));
7992 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 "));
7993 assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04"));
7994 assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04"));
7995 assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04"));
7996 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a"));
7997 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04"));
7998 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04"));
7999 assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04"));
8000
8001 assertThrown!DateTimeException(Date.fromSimpleString("20100704"));
8002 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04"));
8003
8004 assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6));
8005 assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6));
8006 assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6));
8007 assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6));
8008 assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6));
8009 assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6));
8010 }
8011
5fee5ec3 8012 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
8013 @safe unittest
8014 {
8015 import std.conv : to;
8016 import std.meta : AliasSeq;
5fee5ec3 8017 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 8018 {
5fee5ec3 8019 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
8020 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
8021 }
8022 }
8023
8024
8025 /++
8026 Returns the $(LREF Date) farthest in the past which is representable by
8027 $(LREF Date).
8028 +/
8029 @property static Date min() @safe pure nothrow @nogc
8030 {
8031 auto date = Date.init;
8032 date._year = short.min;
8033 date._month = Month.jan;
8034 date._day = 1;
8035
8036 return date;
8037 }
8038
8039 @safe unittest
8040 {
8041 assert(Date.min.year < 0);
8042 assert(Date.min < Date.max);
8043 }
8044
8045
8046 /++
8047 Returns the $(LREF Date) farthest in the future which is representable
8048 by $(LREF Date).
8049 +/
8050 @property static Date max() @safe pure nothrow @nogc
8051 {
8052 auto date = Date.init;
8053 date._year = short.max;
8054 date._month = Month.dec;
8055 date._day = 31;
8056
8057 return date;
8058 }
8059
8060 @safe unittest
8061 {
8062 assert(Date.max.year > 0);
8063 assert(Date.max > Date.min);
8064 }
8065
8066
8067private:
8068
8069 /+
8070 Whether the given values form a valid date.
8071
8072 Params:
8073 year = The year to test.
8074 month = The month of the Gregorian Calendar to test.
8075 day = The day of the month to test.
8076 +/
8077 static bool _valid(int year, int month, int day) @safe pure nothrow @nogc
8078 {
8079 if (!valid!"months"(month))
8080 return false;
8081 return valid!"days"(year, month, day);
8082 }
8083
8084
8085package:
8086
8087 /+
8088 Adds the given number of days to this $(LREF Date). A negative number
8089 will subtract.
8090
8091 The month will be adjusted along with the day if the number of days
8092 added (or subtracted) would overflow (or underflow) the current month.
8093 The year will be adjusted along with the month if the increase (or
8094 decrease) to the month would cause it to overflow (or underflow) the
8095 current year.
8096
5fee5ec3 8097 `_addDays(numDays)` is effectively equivalent to
b4c522fa
IB
8098 $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days).
8099
8100 Params:
8101 days = The number of days to add to this Date.
8102 +/
8103 ref Date _addDays(long days) return @safe pure nothrow @nogc
8104 {
8105 dayOfGregorianCal = cast(int)(dayOfGregorianCal + days);
8106 return this;
8107 }
8108
8109 @safe unittest
8110 {
8111 // Test A.D.
8112 {
8113 auto date = Date(1999, 2, 28);
8114 date._addDays(1);
8115 assert(date == Date(1999, 3, 1));
8116 date._addDays(-1);
8117 assert(date == Date(1999, 2, 28));
8118 }
8119
8120 {
8121 auto date = Date(2000, 2, 28);
8122 date._addDays(1);
8123 assert(date == Date(2000, 2, 29));
8124 date._addDays(1);
8125 assert(date == Date(2000, 3, 1));
8126 date._addDays(-1);
8127 assert(date == Date(2000, 2, 29));
8128 }
8129
8130 {
8131 auto date = Date(1999, 6, 30);
8132 date._addDays(1);
8133 assert(date == Date(1999, 7, 1));
8134 date._addDays(-1);
8135 assert(date == Date(1999, 6, 30));
8136 }
8137
8138 {
8139 auto date = Date(1999, 7, 31);
8140 date._addDays(1);
8141 assert(date == Date(1999, 8, 1));
8142 date._addDays(-1);
8143 assert(date == Date(1999, 7, 31));
8144 }
8145
8146 {
8147 auto date = Date(1999, 1, 1);
8148 date._addDays(-1);
8149 assert(date == Date(1998, 12, 31));
8150 date._addDays(1);
8151 assert(date == Date(1999, 1, 1));
8152 }
8153
8154 {
8155 auto date = Date(1999, 7, 6);
8156 date._addDays(9);
8157 assert(date == Date(1999, 7, 15));
8158 date._addDays(-11);
8159 assert(date == Date(1999, 7, 4));
8160 date._addDays(30);
8161 assert(date == Date(1999, 8, 3));
8162 date._addDays(-3);
8163 assert(date == Date(1999, 7, 31));
8164 }
8165
8166 {
8167 auto date = Date(1999, 7, 6);
8168 date._addDays(365);
8169 assert(date == Date(2000, 7, 5));
8170 date._addDays(-365);
8171 assert(date == Date(1999, 7, 6));
8172 date._addDays(366);
8173 assert(date == Date(2000, 7, 6));
8174 date._addDays(730);
8175 assert(date == Date(2002, 7, 6));
8176 date._addDays(-1096);
8177 assert(date == Date(1999, 7, 6));
8178 }
8179
8180 // Test B.C.
8181 {
8182 auto date = Date(-1999, 2, 28);
8183 date._addDays(1);
8184 assert(date == Date(-1999, 3, 1));
8185 date._addDays(-1);
8186 assert(date == Date(-1999, 2, 28));
8187 }
8188
8189 {
8190 auto date = Date(-2000, 2, 28);
8191 date._addDays(1);
8192 assert(date == Date(-2000, 2, 29));
8193 date._addDays(1);
8194 assert(date == Date(-2000, 3, 1));
8195 date._addDays(-1);
8196 assert(date == Date(-2000, 2, 29));
8197 }
8198
8199 {
8200 auto date = Date(-1999, 6, 30);
8201 date._addDays(1);
8202 assert(date == Date(-1999, 7, 1));
8203 date._addDays(-1);
8204 assert(date == Date(-1999, 6, 30));
8205 }
8206
8207 {
8208 auto date = Date(-1999, 7, 31);
8209 date._addDays(1);
8210 assert(date == Date(-1999, 8, 1));
8211 date._addDays(-1);
8212 assert(date == Date(-1999, 7, 31));
8213 }
8214
8215 {
8216 auto date = Date(-1999, 1, 1);
8217 date._addDays(-1);
8218 assert(date == Date(-2000, 12, 31));
8219 date._addDays(1);
8220 assert(date == Date(-1999, 1, 1));
8221 }
8222
8223 {
8224 auto date = Date(-1999, 7, 6);
8225 date._addDays(9);
8226 assert(date == Date(-1999, 7, 15));
8227 date._addDays(-11);
8228 assert(date == Date(-1999, 7, 4));
8229 date._addDays(30);
8230 assert(date == Date(-1999, 8, 3));
8231 date._addDays(-3);
8232 }
8233
8234 {
8235 auto date = Date(-1999, 7, 6);
8236 date._addDays(365);
8237 assert(date == Date(-1998, 7, 6));
8238 date._addDays(-365);
8239 assert(date == Date(-1999, 7, 6));
8240 date._addDays(366);
8241 assert(date == Date(-1998, 7, 7));
8242 date._addDays(730);
8243 assert(date == Date(-1996, 7, 6));
8244 date._addDays(-1096);
8245 assert(date == Date(-1999, 7, 6));
8246 }
8247
8248 // Test Both
8249 {
8250 auto date = Date(1, 7, 6);
8251 date._addDays(-365);
8252 assert(date == Date(0, 7, 6));
8253 date._addDays(365);
8254 assert(date == Date(1, 7, 6));
8255 date._addDays(-731);
8256 assert(date == Date(-1, 7, 6));
8257 date._addDays(730);
8258 assert(date == Date(1, 7, 5));
8259 }
8260
8261 const cdate = Date(1999, 7, 6);
8262 immutable idate = Date(1999, 7, 6);
8263 static assert(!__traits(compiles, cdate._addDays(12)));
8264 static assert(!__traits(compiles, idate._addDays(12)));
8265 }
8266
8267
8268 @safe pure invariant()
8269 {
8270 import std.format : format;
8271 assert(valid!"months"(_month),
8272 format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8273 assert(valid!"days"(_year, _month, _day),
8274 format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day));
8275 }
8276
8277 short _year = 1;
8278 Month _month = Month.jan;
8279 ubyte _day = 1;
8280}
8281
5fee5ec3
IB
8282///
8283@safe pure unittest
8284{
8285 import core.time : days;
8286
8287 auto d = Date(2000, 6, 1);
8288
8289 assert(d.dayOfYear == 153);
8290 assert(d.dayOfWeek == DayOfWeek.thu);
8291
8292 d += 10.days;
8293 assert(d == Date(2000, 6, 11));
8294
8295 assert(d.toISOExtString() == "2000-06-11");
8296 assert(d.toISOString() == "20000611");
8297 assert(d.toSimpleString() == "2000-Jun-11");
8298
8299 assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
8300 assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
8301 assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
8302}
8303
b4c522fa
IB
8304
8305/++
8306 Represents a time of day with hours, minutes, and seconds. It uses 24 hour
8307 time.
8308+/
8309struct TimeOfDay
8310{
8311public:
8312
8313 /++
8314 Params:
8315 hour = Hour of the day [0 - 24$(RPAREN).
8316 minute = Minute of the hour [0 - 60$(RPAREN).
8317 second = Second of the minute [0 - 60$(RPAREN).
8318
8319 Throws:
8320 $(REF DateTimeException,std,datetime,date) if the resulting
8321 $(LREF TimeOfDay) would be not be valid.
8322 +/
8323 this(int hour, int minute, int second = 0) @safe pure
8324 {
8325 enforceValid!"hours"(hour);
8326 enforceValid!"minutes"(minute);
8327 enforceValid!"seconds"(second);
8328
8329 _hour = cast(ubyte) hour;
8330 _minute = cast(ubyte) minute;
8331 _second = cast(ubyte) second;
8332 }
8333
8334 @safe unittest
8335 {
8336 assert(TimeOfDay(0, 0) == TimeOfDay.init);
8337
8338 {
8339 auto tod = TimeOfDay(0, 0);
8340 assert(tod._hour == 0);
8341 assert(tod._minute == 0);
8342 assert(tod._second == 0);
8343 }
8344
8345 {
8346 auto tod = TimeOfDay(12, 30, 33);
8347 assert(tod._hour == 12);
8348 assert(tod._minute == 30);
8349 assert(tod._second == 33);
8350 }
8351
8352 {
8353 auto tod = TimeOfDay(23, 59, 59);
8354 assert(tod._hour == 23);
8355 assert(tod._minute == 59);
8356 assert(tod._second == 59);
8357 }
8358
8359 assertThrown!DateTimeException(TimeOfDay(24, 0, 0));
8360 assertThrown!DateTimeException(TimeOfDay(0, 60, 0));
8361 assertThrown!DateTimeException(TimeOfDay(0, 0, 60));
8362 }
8363
8364
8365 /++
8366 Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay).
8367
8368 Returns:
8369 $(BOOKTABLE,
8370 $(TR $(TD this &lt; rhs) $(TD &lt; 0))
8371 $(TR $(TD this == rhs) $(TD 0))
8372 $(TR $(TD this &gt; rhs) $(TD &gt; 0))
8373 )
8374 +/
5fee5ec3 8375 int opCmp(TimeOfDay rhs) const @safe pure nothrow @nogc
b4c522fa
IB
8376 {
8377 if (_hour < rhs._hour)
8378 return -1;
8379 if (_hour > rhs._hour)
8380 return 1;
8381
8382 if (_minute < rhs._minute)
8383 return -1;
8384 if (_minute > rhs._minute)
8385 return 1;
8386
8387 if (_second < rhs._second)
8388 return -1;
8389 if (_second > rhs._second)
8390 return 1;
8391
8392 return 0;
8393 }
8394
8395 @safe unittest
8396 {
8397 assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0);
8398
8399 assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0);
8400 assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0);
8401 assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0);
8402 assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8403
8404 assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0);
8405 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0);
8406
8407 assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0);
8408 assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0);
8409
8410 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8411 assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8412 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0);
8413 assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0);
8414 assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0);
8415 assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0);
8416
8417 assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8418 assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0);
8419 assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0);
8420 assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0);
8421
8422 assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0);
8423 assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0);
8424
8425 const ctod = TimeOfDay(12, 30, 33);
8426 immutable itod = TimeOfDay(12, 30, 33);
8427 assert(ctod.opCmp(itod) == 0);
8428 assert(itod.opCmp(ctod) == 0);
8429 }
8430
8431
8432 /++
8433 Hours past midnight.
8434 +/
8435 @property ubyte hour() const @safe pure nothrow @nogc
8436 {
8437 return _hour;
8438 }
8439
8440 @safe unittest
8441 {
8442 assert(TimeOfDay.init.hour == 0);
8443 assert(TimeOfDay(12, 0, 0).hour == 12);
8444
8445 const ctod = TimeOfDay(12, 0, 0);
8446 immutable itod = TimeOfDay(12, 0, 0);
8447 assert(ctod.hour == 12);
8448 assert(itod.hour == 12);
8449 }
8450
8451
8452 /++
8453 Hours past midnight.
8454
8455 Params:
8456 hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to.
8457
8458 Throws:
8459 $(REF DateTimeException,std,datetime,date) if the given hour would
8460 result in an invalid $(LREF TimeOfDay).
8461 +/
8462 @property void hour(int hour) @safe pure
8463 {
8464 enforceValid!"hours"(hour);
8465 _hour = cast(ubyte) hour;
8466 }
8467
8468 @safe unittest
8469 {
8470 assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}());
8471
8472 auto tod = TimeOfDay(0, 0, 0);
8473 tod.hour = 12;
8474 assert(tod == TimeOfDay(12, 0, 0));
8475
8476 const ctod = TimeOfDay(0, 0, 0);
8477 immutable itod = TimeOfDay(0, 0, 0);
8478 static assert(!__traits(compiles, ctod.hour = 12));
8479 static assert(!__traits(compiles, itod.hour = 12));
8480 }
8481
8482
8483 /++
8484 Minutes past the hour.
8485 +/
8486 @property ubyte minute() const @safe pure nothrow @nogc
8487 {
8488 return _minute;
8489 }
8490
8491 @safe unittest
8492 {
8493 assert(TimeOfDay.init.minute == 0);
8494 assert(TimeOfDay(0, 30, 0).minute == 30);
8495
8496 const ctod = TimeOfDay(0, 30, 0);
8497 immutable itod = TimeOfDay(0, 30, 0);
8498 assert(ctod.minute == 30);
8499 assert(itod.minute == 30);
8500 }
8501
8502
8503 /++
8504 Minutes past the hour.
8505
8506 Params:
8507 minute = The minute to set this $(LREF TimeOfDay)'s minute to.
8508
8509 Throws:
8510 $(REF DateTimeException,std,datetime,date) if the given minute
8511 would result in an invalid $(LREF TimeOfDay).
8512 +/
8513 @property void minute(int minute) @safe pure
8514 {
8515 enforceValid!"minutes"(minute);
8516 _minute = cast(ubyte) minute;
8517 }
8518
8519 @safe unittest
8520 {
8521 assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}());
8522
8523 auto tod = TimeOfDay(0, 0, 0);
8524 tod.minute = 30;
8525 assert(tod == TimeOfDay(0, 30, 0));
8526
8527 const ctod = TimeOfDay(0, 0, 0);
8528 immutable itod = TimeOfDay(0, 0, 0);
8529 static assert(!__traits(compiles, ctod.minute = 30));
8530 static assert(!__traits(compiles, itod.minute = 30));
8531 }
8532
8533
8534 /++
8535 Seconds past the minute.
8536 +/
8537 @property ubyte second() const @safe pure nothrow @nogc
8538 {
8539 return _second;
8540 }
8541
8542 @safe unittest
8543 {
8544 assert(TimeOfDay.init.second == 0);
8545 assert(TimeOfDay(0, 0, 33).second == 33);
8546
8547 const ctod = TimeOfDay(0, 0, 33);
8548 immutable itod = TimeOfDay(0, 0, 33);
8549 assert(ctod.second == 33);
8550 assert(itod.second == 33);
8551 }
8552
8553
8554 /++
8555 Seconds past the minute.
8556
8557 Params:
8558 second = The second to set this $(LREF TimeOfDay)'s second to.
8559
8560 Throws:
8561 $(REF DateTimeException,std,datetime,date) if the given second
8562 would result in an invalid $(LREF TimeOfDay).
8563 +/
8564 @property void second(int second) @safe pure
8565 {
8566 enforceValid!"seconds"(second);
8567 _second = cast(ubyte) second;
8568 }
8569
8570 @safe unittest
8571 {
8572 assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}());
8573
8574 auto tod = TimeOfDay(0, 0, 0);
8575 tod.second = 33;
8576 assert(tod == TimeOfDay(0, 0, 33));
8577
8578 const ctod = TimeOfDay(0, 0, 0);
8579 immutable itod = TimeOfDay(0, 0, 0);
8580 static assert(!__traits(compiles, ctod.second = 33));
8581 static assert(!__traits(compiles, itod.second = 33));
8582 }
8583
8584
8585 /++
5fee5ec3
IB
8586 Adds the given number of units to this $(LREF TimeOfDay), mutating it. A
8587 negative number will subtract.
b4c522fa
IB
8588
8589 The difference between rolling and adding is that rolling does not
8590 affect larger units. For instance, rolling a $(LREF TimeOfDay)
8591 one hours's worth of minutes gets the exact same
8592 $(LREF TimeOfDay).
8593
5fee5ec3 8594 Accepted units are `"hours"`, `"minutes"`, and `"seconds"`.
b4c522fa
IB
8595
8596 Params:
8597 units = The units to add.
8598 value = The number of $(D_PARAM units) to add to this
8599 $(LREF TimeOfDay).
5fee5ec3
IB
8600
8601 Returns:
8602 A reference to the `TimeOfDay` (`this`).
b4c522fa
IB
8603 +/
8604 ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8605 if (units == "hours")
8606 {
5fee5ec3 8607 import core.time : dur;
b4c522fa
IB
8608 return this += dur!"hours"(value);
8609 }
8610
8611 ///
8612 @safe unittest
8613 {
8614 auto tod1 = TimeOfDay(7, 12, 0);
8615 tod1.roll!"hours"(1);
8616 assert(tod1 == TimeOfDay(8, 12, 0));
8617
8618 auto tod2 = TimeOfDay(7, 12, 0);
8619 tod2.roll!"hours"(-1);
8620 assert(tod2 == TimeOfDay(6, 12, 0));
8621
8622 auto tod3 = TimeOfDay(23, 59, 0);
8623 tod3.roll!"minutes"(1);
8624 assert(tod3 == TimeOfDay(23, 0, 0));
8625
8626 auto tod4 = TimeOfDay(0, 0, 0);
8627 tod4.roll!"minutes"(-1);
8628 assert(tod4 == TimeOfDay(0, 59, 0));
8629
8630 auto tod5 = TimeOfDay(23, 59, 59);
8631 tod5.roll!"seconds"(1);
8632 assert(tod5 == TimeOfDay(23, 59, 0));
8633
8634 auto tod6 = TimeOfDay(0, 0, 0);
8635 tod6.roll!"seconds"(-1);
8636 assert(tod6 == TimeOfDay(0, 0, 59));
8637 }
8638
8639 @safe unittest
8640 {
8641 auto tod = TimeOfDay(12, 27, 2);
8642 tod.roll!"hours"(22).roll!"hours"(-7);
8643 assert(tod == TimeOfDay(3, 27, 2));
8644
8645 const ctod = TimeOfDay(0, 0, 0);
8646 immutable itod = TimeOfDay(0, 0, 0);
8647 static assert(!__traits(compiles, ctod.roll!"hours"(53)));
8648 static assert(!__traits(compiles, itod.roll!"hours"(53)));
8649 }
8650
8651
5fee5ec3 8652 /// ditto
b4c522fa
IB
8653 ref TimeOfDay roll(string units)(long value) @safe pure nothrow @nogc
8654 if (units == "minutes" || units == "seconds")
8655 {
8656 import std.format : format;
8657
8658 enum memberVarStr = units[0 .. $ - 1];
8659 value %= 60;
8660 mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr));
8661
8662 if (value < 0)
8663 {
8664 if (newVal < 0)
8665 newVal += 60;
8666 }
8667 else if (newVal >= 60)
8668 newVal -= 60;
8669
8670 mixin(format("_%s = cast(ubyte) newVal;", memberVarStr));
8671 return this;
8672 }
8673
8674 // Test roll!"minutes"().
8675 @safe unittest
8676 {
5fee5ec3 8677 static void testTOD(TimeOfDay orig, int minutes, TimeOfDay expected, size_t line = __LINE__)
b4c522fa
IB
8678 {
8679 orig.roll!"minutes"(minutes);
8680 assert(orig == expected);
8681 }
8682
8683 testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8684 testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33));
8685 testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33));
8686 testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33));
8687 testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33));
8688 testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33));
8689 testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33));
8690 testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33));
8691 testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33));
8692 testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33));
8693 testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33));
8694 testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8695 testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33));
8696 testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33));
8697 testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33));
8698
8699 testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33));
8700 testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33));
8701 testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33));
8702 testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33));
8703 testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33));
8704 testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33));
8705 testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33));
8706 testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33));
8707
8708 testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33));
8709 testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33));
8710 testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33));
8711 testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33));
8712 testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33));
8713 testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33));
8714 testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33));
8715 testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33));
8716 testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33));
8717 testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33));
8718 testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8719 testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33));
8720 testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33));
8721 testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33));
8722
8723 testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33));
8724 testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33));
8725 testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33));
8726 testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33));
8727 testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33));
8728 testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33));
8729 testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33));
8730 testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33));
8731
8732 testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33));
8733 testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33));
8734 testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33));
8735
8736 testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33));
8737 testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33));
8738 testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33));
8739
8740 testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33));
8741 testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33));
8742 testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33));
8743
8744 testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33));
8745 testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33));
8746 testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33));
8747
8748 auto tod = TimeOfDay(12, 27, 2);
8749 tod.roll!"minutes"(97).roll!"minutes"(-102);
8750 assert(tod == TimeOfDay(12, 22, 2));
8751
8752 const ctod = TimeOfDay(0, 0, 0);
8753 immutable itod = TimeOfDay(0, 0, 0);
8754 static assert(!__traits(compiles, ctod.roll!"minutes"(7)));
8755 static assert(!__traits(compiles, itod.roll!"minutes"(7)));
8756 }
8757
8758 // Test roll!"seconds"().
8759 @safe unittest
8760 {
5fee5ec3 8761 static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
b4c522fa
IB
8762 {
8763 orig.roll!"seconds"(seconds);
8764 assert(orig == expected);
8765 }
8766
8767 testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
8768 testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
8769 testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
8770 testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
8771 testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
8772 testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
8773 testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
8774 testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
8775 testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
8776 testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0));
8777 testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3));
8778 testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32));
8779 testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33));
8780 testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34));
8781
8782 testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59));
8783 testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0));
8784 testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1));
8785 testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0));
8786 testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32));
8787 testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33));
8788 testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34));
8789 testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33));
8790
8791 testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
8792 testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
8793 testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
8794 testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
8795 testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
8796 testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
8797 testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
8798 testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
8799 testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59));
8800 testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58));
8801 testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34));
8802 testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33));
8803 testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32));
8804
8805 testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
8806 testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
8807 testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59));
8808
8809 testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
8810 testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
8811 testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59));
8812
8813 testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
8814 testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
8815 testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59));
8816
8817 testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0));
8818 testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
8819 testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
8820
8821 auto tod = TimeOfDay(12, 27, 2);
8822 tod.roll!"seconds"(105).roll!"seconds"(-77);
8823 assert(tod == TimeOfDay(12, 27, 30));
8824
8825 const ctod = TimeOfDay(0, 0, 0);
8826 immutable itod = TimeOfDay(0, 0, 0);
8827 static assert(!__traits(compiles, ctod.roll!"seconds"(7)));
8828 static assert(!__traits(compiles, itod.roll!"seconds"(7)));
8829 }
8830
8831
5fee5ec3 8832 import core.time : Duration;
b4c522fa
IB
8833 /++
8834 Gives the result of adding or subtracting a $(REF Duration, core,time)
8835 from this $(LREF TimeOfDay).
8836
8837 The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8838 are
8839
8840 $(BOOKTABLE,
8841 $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8842 $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8843 )
8844
8845 Params:
8846 duration = The $(REF Duration, core,time) to add to or subtract from
8847 this $(LREF TimeOfDay).
8848 +/
8849 TimeOfDay opBinary(string op)(Duration duration) const @safe pure nothrow @nogc
8850 if (op == "+" || op == "-")
8851 {
8852 TimeOfDay retval = this;
8853 immutable seconds = duration.total!"seconds";
8854 mixin("return retval._addSeconds(" ~ op ~ "seconds);");
8855 }
8856
8857 ///
8858 @safe unittest
8859 {
8860 import core.time : hours, minutes, seconds;
8861
8862 assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
8863 assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
8864 assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
8865 assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
8866
8867 assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
8868 assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
8869 assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
8870 assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
8871 }
8872
8873 @safe unittest
8874 {
8875 auto tod = TimeOfDay(12, 30, 33);
8876
5fee5ec3 8877 import core.time : dur;
b4c522fa
IB
8878 assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8879 assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8880 assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8881 assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8882 assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8883 assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8884
8885 assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8886 assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8887 assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8888 assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8889 assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8890 assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8891
8892 assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8893 assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8894 assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8895 assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8896 assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8897 assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8898
8899 assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8900 assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8901 assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8902 assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8903 assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8904 assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8905
8906 auto duration = dur!"hours"(11);
8907 const ctod = TimeOfDay(12, 30, 33);
8908 immutable itod = TimeOfDay(12, 30, 33);
8909 assert(tod + duration == TimeOfDay(23, 30, 33));
8910 assert(ctod + duration == TimeOfDay(23, 30, 33));
8911 assert(itod + duration == TimeOfDay(23, 30, 33));
8912
8913 assert(tod - duration == TimeOfDay(1, 30, 33));
8914 assert(ctod - duration == TimeOfDay(1, 30, 33));
8915 assert(itod - duration == TimeOfDay(1, 30, 33));
8916 }
8917
b4c522fa
IB
8918
8919 /++
8920 Gives the result of adding or subtracting a $(REF Duration, core,time)
8921 from this $(LREF TimeOfDay), as well as assigning the result to this
8922 $(LREF TimeOfDay).
8923
8924 The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8925 are
8926
8927 $(BOOKTABLE,
8928 $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8929 $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
8930 )
8931
8932 Params:
8933 duration = The $(REF Duration, core,time) to add to or subtract from
8934 this $(LREF TimeOfDay).
8935 +/
8936 ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow @nogc
8937 if (op == "+" || op == "-")
8938 {
8939 immutable seconds = duration.total!"seconds";
8940 mixin("return _addSeconds(" ~ op ~ "seconds);");
8941 }
8942
8943 @safe unittest
8944 {
5fee5ec3 8945 import core.time : dur;
b4c522fa
IB
8946 auto duration = dur!"hours"(12);
8947
8948 assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33));
8949 assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33));
8950 assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33));
8951 assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33));
8952 assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40));
8953 assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26));
8954
8955 assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40));
8956 assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26));
8957 assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40));
8958 assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26));
8959 assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40));
8960 assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26));
8961
8962 assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33));
8963 assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33));
8964 assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33));
8965 assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33));
8966 assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40));
8967 assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26));
8968
8969 assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40));
8970 assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26));
8971 assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40));
8972 assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26));
8973 assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40));
8974 assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26));
8975
8976 auto tod = TimeOfDay(19, 17, 22);
8977 (tod += dur!"seconds"(9)) += dur!"seconds"(-7292);
8978 assert(tod == TimeOfDay(17, 15, 59));
8979
8980 const ctod = TimeOfDay(12, 33, 30);
8981 immutable itod = TimeOfDay(12, 33, 30);
8982 static assert(!__traits(compiles, ctod += duration));
8983 static assert(!__traits(compiles, itod += duration));
8984 static assert(!__traits(compiles, ctod -= duration));
8985 static assert(!__traits(compiles, itod -= duration));
8986 }
8987
b4c522fa
IB
8988
8989 /++
8990 Gives the difference between two $(LREF TimeOfDay)s.
8991
8992 The legal types of arithmetic for $(LREF TimeOfDay) using this operator
8993 are
8994
8995 $(BOOKTABLE,
8996 $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration))
8997 )
8998
8999 Params:
9000 rhs = The $(LREF TimeOfDay) to subtract from this one.
9001 +/
5fee5ec3 9002 Duration opBinary(string op)(TimeOfDay rhs) const @safe pure nothrow @nogc
b4c522fa
IB
9003 if (op == "-")
9004 {
9005 immutable lhsSec = _hour * 3600 + _minute * 60 + _second;
9006 immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second;
9007
5fee5ec3 9008 import core.time : dur;
b4c522fa
IB
9009 return dur!"seconds"(lhsSec - rhsSec);
9010 }
9011
9012 @safe unittest
9013 {
9014 auto tod = TimeOfDay(12, 30, 33);
9015
5fee5ec3 9016 import core.time : dur;
b4c522fa
IB
9017 assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061));
9018 assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061));
9019 assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200));
9020 assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200));
9021 assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240));
9022 assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240));
9023 assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1));
9024 assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1));
9025
9026 const ctod = TimeOfDay(12, 30, 33);
9027 immutable itod = TimeOfDay(12, 30, 33);
9028 assert(tod - tod == Duration.zero);
9029 assert(ctod - tod == Duration.zero);
9030 assert(itod - tod == Duration.zero);
9031
9032 assert(tod - ctod == Duration.zero);
9033 assert(ctod - ctod == Duration.zero);
9034 assert(itod - ctod == Duration.zero);
9035
9036 assert(tod - itod == Duration.zero);
9037 assert(ctod - itod == Duration.zero);
9038 assert(itod - itod == Duration.zero);
9039 }
9040
9041
9042 /++
5fee5ec3
IB
9043 Converts this $(LREF TimeOfDay) to a string with the format `HHMMSS`.
9044 If `writer` is set, the resulting string will be written directly to it.
9045
9046 Params:
9047 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9048 Returns:
9049 A `string` when not using an output range; `void` otherwise.
b4c522fa
IB
9050 +/
9051 string toISOString() const @safe pure nothrow
9052 {
5fee5ec3
IB
9053 import std.array : appender;
9054 auto w = appender!string();
9055 w.reserve(6);
b4c522fa 9056 try
5fee5ec3 9057 toISOString(w);
b4c522fa 9058 catch (Exception e)
5fee5ec3
IB
9059 assert(0, "toISOString() threw.");
9060 return w.data;
9061 }
9062
9063 /// ditto
9064 void toISOString(W)(ref W writer) const
9065 if (isOutputRange!(W, char))
9066 {
9067 import std.format.write : formattedWrite;
9068 formattedWrite(writer, "%02d%02d%02d", _hour, _minute, _second);
b4c522fa
IB
9069 }
9070
9071 ///
9072 @safe unittest
9073 {
9074 assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
9075 assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
9076 }
9077
9078 @safe unittest
9079 {
9080 auto tod = TimeOfDay(12, 30, 33);
9081 const ctod = TimeOfDay(12, 30, 33);
9082 immutable itod = TimeOfDay(12, 30, 33);
9083 assert(tod.toISOString() == "123033");
9084 assert(ctod.toISOString() == "123033");
9085 assert(itod.toISOString() == "123033");
9086 }
9087
9088
9089 /++
5fee5ec3
IB
9090 Converts this $(LREF TimeOfDay) to a string with the format `HH:MM:SS`.
9091 If `writer` is set, the resulting string will be written directly to it.
9092
9093 Params:
9094 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9095 Returns:
9096 A `string` when not using an output range; `void` otherwise.
b4c522fa
IB
9097 +/
9098 string toISOExtString() const @safe pure nothrow
9099 {
5fee5ec3
IB
9100 import std.array : appender;
9101 auto w = appender!string();
9102 w.reserve(8);
b4c522fa 9103 try
5fee5ec3 9104 toISOExtString(w);
b4c522fa 9105 catch (Exception e)
5fee5ec3
IB
9106 assert(0, "toISOExtString() threw.");
9107 return w.data;
9108 }
9109
9110 /// ditto
9111 void toISOExtString(W)(ref W writer) const
9112 if (isOutputRange!(W, char))
9113 {
9114 import std.format.write : formattedWrite;
9115 formattedWrite(writer, "%02d:%02d:%02d", _hour, _minute, _second);
b4c522fa
IB
9116 }
9117
9118 ///
9119 @safe unittest
9120 {
9121 assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
9122 assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
9123 }
9124
9125 @safe unittest
9126 {
9127 auto tod = TimeOfDay(12, 30, 33);
9128 const ctod = TimeOfDay(12, 30, 33);
9129 immutable itod = TimeOfDay(12, 30, 33);
9130 assert(tod.toISOExtString() == "12:30:33");
9131 assert(ctod.toISOExtString() == "12:30:33");
9132 assert(itod.toISOExtString() == "12:30:33");
9133 }
9134
9135
9136 /++
9137 Converts this TimeOfDay to a string.
9138
9139 This function exists to make it easy to convert a $(LREF TimeOfDay) to a
9140 string for code that does not care what the exact format is - just that
9141 it presents the information in a clear manner. It also makes it easy to
9142 simply convert a $(LREF TimeOfDay) to a string when using functions such
9143 as `to!string`, `format`, or `writeln` which use toString to convert
9144 user-defined types. So, it is unlikely that much code will call
9145 toString directly.
9146
9147 The format of the string is purposefully unspecified, and code that
9148 cares about the format of the string should use `toISOString`,
9149 `toISOExtString`, or some other custom formatting function that
9150 explicitly generates the format that the code needs. The reason is that
9151 the code is then clear about what format it's using, making it less
9152 error-prone to maintain the code and interact with other software that
9153 consumes the generated strings. It's for this same reason that
9154 $(LREF TimeOfDay) has no `fromString` function, whereas it does have
9155 `fromISOString` and `fromISOExtString`.
9156
9157 The format returned by toString may or may not change in the future.
5fee5ec3
IB
9158
9159 Params:
9160 writer = A `char` accepting $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
9161 Returns:
9162 A `string` when not using an output range; `void` otherwise.
b4c522fa
IB
9163 +/
9164 string toString() const @safe pure nothrow
9165 {
9166 return toISOExtString();
9167 }
9168
5fee5ec3
IB
9169 /// ditto
9170 void toString(W)(ref W writer) const
9171 if (isOutputRange!(W, char))
9172 {
9173 toISOExtString(writer);
9174 }
9175
b4c522fa
IB
9176 @safe unittest
9177 {
9178 auto tod = TimeOfDay(12, 30, 33);
9179 const ctod = TimeOfDay(12, 30, 33);
9180 immutable itod = TimeOfDay(12, 30, 33);
9181 assert(tod.toString());
9182 assert(ctod.toString());
9183 assert(itod.toString());
9184 }
9185
9186
9187 /++
9188 Creates a $(LREF TimeOfDay) from a string with the format HHMMSS.
9189 Whitespace is stripped from the given string.
9190
9191 Params:
9192 isoString = A string formatted in the ISO format for times.
9193
9194 Throws:
9195 $(REF DateTimeException,std,datetime,date) if the given string is
9196 not in the ISO format or if the resulting $(LREF TimeOfDay) would
9197 not be valid.
9198 +/
5fee5ec3 9199 static TimeOfDay fromISOString(S)(scope const S isoString) @safe pure
b4c522fa
IB
9200 if (isSomeString!S)
9201 {
9202 import std.conv : to, text, ConvException;
9203 import std.exception : enforce;
9204 import std.string : strip;
9205
9206 int hours, minutes, seconds;
9207 auto str = strip(isoString);
9208
9209 enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString));
9210
9211 try
9212 {
9213 // cast to int from uint is used because it checks for
9214 // non digits without extra loops
9215 hours = cast(int) to!uint(str[0 .. 2]);
9216 minutes = cast(int) to!uint(str[2 .. 4]);
9217 seconds = cast(int) to!uint(str[4 .. $]);
9218 }
9219 catch (ConvException)
9220 {
9221 throw new DateTimeException(text("Invalid ISO String: ", isoString));
9222 }
9223
9224 return TimeOfDay(hours, minutes, seconds);
9225 }
9226
9227 ///
9228 @safe unittest
9229 {
9230 assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
9231 assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
9232 assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
9233 }
9234
9235 @safe unittest
9236 {
9237 assertThrown!DateTimeException(TimeOfDay.fromISOString(""));
9238 assertThrown!DateTimeException(TimeOfDay.fromISOString("0"));
9239 assertThrown!DateTimeException(TimeOfDay.fromISOString("00"));
9240 assertThrown!DateTimeException(TimeOfDay.fromISOString("000"));
9241 assertThrown!DateTimeException(TimeOfDay.fromISOString("0000"));
9242 assertThrown!DateTimeException(TimeOfDay.fromISOString("00000"));
9243 assertThrown!DateTimeException(TimeOfDay.fromISOString("13033"));
9244 assertThrown!DateTimeException(TimeOfDay.fromISOString("1277"));
9245 assertThrown!DateTimeException(TimeOfDay.fromISOString("12707"));
9246 assertThrown!DateTimeException(TimeOfDay.fromISOString("12070"));
9247 assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a"));
9248 assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3"));
9249 assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33"));
9250 assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033"));
9251 assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033"));
9252 assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033"));
9253 assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330"));
9254 assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033"));
9255 assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033"));
9256 assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033"));
9257 assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am"));
9258 assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm"));
9259
9260 assertThrown!DateTimeException(TimeOfDay.fromISOString("0::"));
9261 assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:"));
9262 assertThrown!DateTimeException(TimeOfDay.fromISOString("::0"));
9263 assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0"));
9264 assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00"));
9265 assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0"));
9266 assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0"));
9267 assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0"));
9268 assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00"));
9269 assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33"));
9270 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7"));
9271 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07"));
9272 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0"));
9273 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a"));
9274 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3"));
9275 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33"));
9276 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33"));
9277 assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33"));
9278 assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33"));
9279 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30"));
9280 assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30"));
9281 assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33"));
9282 assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33"));
9283 assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33"));
9284 assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33"));
9285 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am"));
9286 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm"));
9287
9288 assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33"));
9289
9290 assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17));
9291 assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12));
9292 assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7));
9293 assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17));
9294 assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17));
9295 assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17));
9296 }
9297
5fee5ec3 9298 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
9299 @safe unittest
9300 {
9301 import std.conv : to;
9302 import std.meta : AliasSeq;
5fee5ec3 9303 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 9304 {
5fee5ec3 9305 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
9306 assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
9307 }
9308 }
9309
9310
9311 /++
9312 Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS.
9313 Whitespace is stripped from the given string.
9314
9315 Params:
9316 isoExtString = A string formatted in the ISO Extended format for
9317 times.
9318
9319 Throws:
9320 $(REF DateTimeException,std,datetime,date) if the given string is
9321 not in the ISO Extended format or if the resulting $(LREF TimeOfDay)
9322 would not be valid.
9323 +/
5fee5ec3 9324 static TimeOfDay fromISOExtString(S)(scope const S isoExtString) @safe pure
b4c522fa
IB
9325 if (isSomeString!S)
9326 {
5fee5ec3 9327 import std.conv : ConvException, text, to;
b4c522fa
IB
9328 import std.string : strip;
9329
5fee5ec3
IB
9330 auto str = strip(isoExtString);
9331 int hours, minutes, seconds;
b4c522fa 9332
5fee5ec3
IB
9333 if (str.length != 8 || str[2] != ':' || str[5] != ':')
9334 throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
b4c522fa 9335
5fee5ec3
IB
9336 try
9337 {
9338 // cast to int from uint is used because it checks for
9339 // non digits without extra loops
9340 hours = cast(int) to!uint(str[0 .. 2]);
9341 minutes = cast(int) to!uint(str[3 .. 5]);
9342 seconds = cast(int) to!uint(str[6 .. $]);
9343 }
9344 catch (ConvException)
9345 {
9346 throw new DateTimeException(text("Invalid ISO Extended String: ", isoExtString));
9347 }
b4c522fa 9348
5fee5ec3 9349 return TimeOfDay(hours, minutes, seconds);
b4c522fa
IB
9350 }
9351
9352 ///
9353 @safe unittest
9354 {
9355 assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
9356 assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
9357 assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
9358 }
9359
9360 @safe unittest
9361 {
9362 assertThrown!DateTimeException(TimeOfDay.fromISOExtString(""));
9363 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0"));
9364 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00"));
9365 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000"));
9366 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000"));
9367 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000"));
9368 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033"));
9369 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277"));
9370 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707"));
9371 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070"));
9372 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a"));
9373 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3"));
9374 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33"));
9375 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033"));
9376 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033"));
9377 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033"));
9378 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330"));
9379 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033"));
9380 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033"));
9381 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033"));
9382 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am"));
9383 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm"));
9384
9385 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::"));
9386 assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:"));
9387 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0"));
9388 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0"));
9389 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00"));
9390 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0"));
9391 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0"));
9392 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0"));
9393 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00"));
9394 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33"));
9395 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7"));
9396 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07"));
9397 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0"));
9398 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a"));
9399 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3"));
9400 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33"));
9401 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33"));
9402 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33"));
9403 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33"));
9404 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30"));
9405 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30"));
9406 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33"));
9407 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33"));
9408 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33"));
9409 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33"));
9410 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am"));
9411 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm"));
9412
9413 assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033"));
9414
9415 assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17));
9416 assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12));
9417 assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7));
9418 assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17));
9419 assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17));
9420 assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17));
9421 }
9422
5fee5ec3 9423 // https://issues.dlang.org/show_bug.cgi?id=17801
b4c522fa
IB
9424 @safe unittest
9425 {
9426 import std.conv : to;
9427 import std.meta : AliasSeq;
5fee5ec3 9428 static foreach (C; AliasSeq!(char, wchar, dchar))
b4c522fa 9429 {
5fee5ec3 9430 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
b4c522fa
IB
9431 assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
9432 }
9433 }
9434
9435
9436 /++
9437 Returns midnight.
9438 +/
9439 @property static TimeOfDay min() @safe pure nothrow @nogc
9440 {
9441 return TimeOfDay.init;
9442 }
9443
9444 @safe unittest
9445 {
9446 assert(TimeOfDay.min.hour == 0);
9447 assert(TimeOfDay.min.minute == 0);
9448 assert(TimeOfDay.min.second == 0);
9449 assert(TimeOfDay.min < TimeOfDay.max);
9450 }
9451
9452
9453 /++
9454 Returns one second short of midnight.
9455 +/
9456 @property static TimeOfDay max() @safe pure nothrow @nogc
9457 {
9458 auto tod = TimeOfDay.init;
9459 tod._hour = maxHour;
9460 tod._minute = maxMinute;
9461 tod._second = maxSecond;
9462
9463 return tod;
9464 }
9465
9466 @safe unittest
9467 {
9468 assert(TimeOfDay.max.hour == 23);
9469 assert(TimeOfDay.max.minute == 59);
9470 assert(TimeOfDay.max.second == 59);
9471 assert(TimeOfDay.max > TimeOfDay.min);
9472 }
9473
9474
9475private:
9476
9477 /+
9478 Add seconds to the time of day. Negative values will subtract. If the
9479 number of seconds overflows (or underflows), then the seconds will wrap,
9480 increasing (or decreasing) the number of minutes accordingly. If the
9481 number of minutes overflows (or underflows), then the minutes will wrap.
9482 If the number of minutes overflows(or underflows), then the hour will
9483 wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30).
9484
9485 Params:
9486 seconds = The number of seconds to add to this TimeOfDay.
9487 +/
9488 ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow @nogc
9489 {
5fee5ec3 9490 import core.time : convert;
b4c522fa
IB
9491 long hnsecs = convert!("seconds", "hnsecs")(seconds);
9492 hnsecs += convert!("hours", "hnsecs")(_hour);
9493 hnsecs += convert!("minutes", "hnsecs")(_minute);
9494 hnsecs += convert!("seconds", "hnsecs")(_second);
9495
9496 hnsecs %= convert!("days", "hnsecs")(1);
9497
9498 if (hnsecs < 0)
9499 hnsecs += convert!("days", "hnsecs")(1);
9500
9501 immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs);
9502 immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
9503 immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs);
9504
9505 _hour = cast(ubyte) newHours;
9506 _minute = cast(ubyte) newMinutes;
9507 _second = cast(ubyte) newSeconds;
9508
9509 return this;
9510 }
9511
9512 @safe unittest
9513 {
5fee5ec3 9514 static void testTOD(TimeOfDay orig, int seconds, TimeOfDay expected, size_t line = __LINE__)
b4c522fa
IB
9515 {
9516 orig._addSeconds(seconds);
9517 assert(orig == expected);
9518 }
9519
9520 testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33));
9521 testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34));
9522 testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35));
9523 testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36));
9524 testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37));
9525 testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38));
9526 testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43));
9527 testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48));
9528 testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59));
9529 testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0));
9530 testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3));
9531 testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32));
9532 testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33));
9533 testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34));
9534
9535 testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59));
9536 testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0));
9537 testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1));
9538 testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0));
9539 testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32));
9540 testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33));
9541 testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34));
9542 testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33));
9543
9544 testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32));
9545 testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31));
9546 testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30));
9547 testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29));
9548 testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28));
9549 testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23));
9550 testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18));
9551 testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0));
9552 testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59));
9553 testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58));
9554 testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34));
9555 testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33));
9556 testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32));
9557
9558 testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0));
9559 testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59));
9560 testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33));
9561 testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32));
9562 testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59));
9563 testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33));
9564
9565 testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1));
9566 testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0));
9567 testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59));
9568
9569 testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1));
9570 testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0));
9571 testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59));
9572
9573 testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1));
9574 testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0));
9575 testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59));
9576
9577 testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0));
9578 testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59));
9579 testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58));
9580
9581 const ctod = TimeOfDay(0, 0, 0);
9582 immutable itod = TimeOfDay(0, 0, 0);
9583 static assert(!__traits(compiles, ctod._addSeconds(7)));
9584 static assert(!__traits(compiles, itod._addSeconds(7)));
9585 }
9586
9587
9588 /+
9589 Whether the given values form a valid $(LREF TimeOfDay).
9590 +/
9591 static bool _valid(int hour, int minute, int second) @safe pure nothrow @nogc
9592 {
9593 return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second);
9594 }
9595
9596
9597 @safe pure invariant()
9598 {
9599 import std.format : format;
9600 assert(_valid(_hour, _minute, _second),
9601 format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second));
9602 }
9603
9604
9605package:
9606
9607 ubyte _hour;
9608 ubyte _minute;
9609 ubyte _second;
9610
9611 enum ubyte maxHour = 24 - 1;
9612 enum ubyte maxMinute = 60 - 1;
9613 enum ubyte maxSecond = 60 - 1;
9614}
9615
5fee5ec3
IB
9616///
9617@safe pure unittest
9618{
9619 import core.time : minutes, seconds;
9620
9621 auto t = TimeOfDay(12, 30, 0);
9622
9623 t += 10.minutes + 100.seconds;
9624 assert(t == TimeOfDay(12, 41, 40));
9625
9626 assert(t.toISOExtString() == "12:41:40");
9627 assert(t.toISOString() == "124140");
9628
9629 assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
9630 assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
9631}
b4c522fa
IB
9632
9633/++
9634 Returns whether the given value is valid for the given unit type when in a
9635 time point. Naturally, a duration is not held to a particular range, but
9636 the values in a time point are (e.g. a month must be in the range of
9637 1 - 12 inclusive).
9638
9639 Params:
9640 units = The units of time to validate.
9641 value = The number to validate.
9642 +/
9643bool valid(string units)(int value) @safe pure nothrow @nogc
9644if (units == "months" ||
9645 units == "hours" ||
9646 units == "minutes" ||
9647 units == "seconds")
9648{
9649 static if (units == "months")
9650 return value >= Month.jan && value <= Month.dec;
9651 else static if (units == "hours")
9652 return value >= 0 && value <= 23;
9653 else static if (units == "minutes")
9654 return value >= 0 && value <= 59;
9655 else static if (units == "seconds")
9656 return value >= 0 && value <= 59;
9657}
9658
9659///
9660@safe unittest
9661{
9662 assert(valid!"hours"(12));
9663 assert(!valid!"hours"(32));
9664 assert(valid!"months"(12));
9665 assert(!valid!"months"(13));
9666}
9667
9668/++
9669 Returns whether the given day is valid for the given year and month.
9670
9671 Params:
9672 units = The units of time to validate.
9673 year = The year of the day to validate.
9674 month = The month of the day to validate (January is 1).
9675 day = The day to validate.
9676 +/
9677bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
9678if (units == "days")
9679{
9680 return day > 0 && day <= maxDay(year, month);
9681}
9682
9683///
9684@safe pure nothrow @nogc unittest
9685{
9686 assert(valid!"days"(2016, 2, 29));
9687 assert(!valid!"days"(2016, 2, 30));
9688 assert(valid!"days"(2017, 2, 20));
9689 assert(!valid!"days"(2017, 2, 29));
9690}
9691
9692
9693/++
9694 Params:
9695 units = The units of time to validate.
9696 value = The number to validate.
9697 file = The file that the $(LREF DateTimeException) will list if thrown.
9698 line = The line number that the $(LREF DateTimeException) will list if
9699 thrown.
9700
9701 Throws:
5fee5ec3 9702 $(LREF DateTimeException) if `valid!units(value)` is false.
b4c522fa
IB
9703 +/
9704void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure
9705if (units == "months" ||
9706 units == "hours" ||
9707 units == "minutes" ||
9708 units == "seconds")
9709{
9710 import std.format : format;
9711
9712 static if (units == "months")
9713 {
9714 if (!valid!units(value))
9715 throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line);
9716 }
9717 else static if (units == "hours")
9718 {
9719 if (!valid!units(value))
9720 throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line);
9721 }
9722 else static if (units == "minutes")
9723 {
9724 if (!valid!units(value))
9725 throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line);
9726 }
9727 else static if (units == "seconds")
9728 {
9729 if (!valid!units(value))
9730 throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line);
9731 }
9732}
9733
5fee5ec3
IB
9734///
9735@safe pure unittest
9736{
9737 import std.exception : assertThrown, assertNotThrown;
9738
9739 assertNotThrown(enforceValid!"months"(10));
9740 assertNotThrown(enforceValid!"seconds"(40));
9741
9742 assertThrown!DateTimeException(enforceValid!"months"(0));
9743 assertThrown!DateTimeException(enforceValid!"hours"(24));
9744 assertThrown!DateTimeException(enforceValid!"minutes"(60));
9745 assertThrown!DateTimeException(enforceValid!"seconds"(60));
9746}
9747
b4c522fa
IB
9748
9749/++
5fee5ec3
IB
9750 Because the validity of the day number depends on both on the year
9751 and month of which the day is occurring, take all three variables
9752 to validate the day.
9753
b4c522fa
IB
9754 Params:
9755 units = The units of time to validate.
9756 year = The year of the day to validate.
9757 month = The month of the day to validate.
9758 day = The day to validate.
9759 file = The file that the $(LREF DateTimeException) will list if thrown.
9760 line = The line number that the $(LREF DateTimeException) will list if
9761 thrown.
9762
9763 Throws:
9764 $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false.
9765 +/
9766void enforceValid(string units)
9767 (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure
9768if (units == "days")
9769{
9770 import std.format : format;
9771 if (!valid!"days"(year, month, day))
9772 throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line);
9773}
9774
5fee5ec3
IB
9775///
9776@safe pure unittest
9777{
9778 import std.exception : assertThrown, assertNotThrown;
9779
9780 assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
9781 // leap year
9782 assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
9783
9784 assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
9785 assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
9786 assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
9787}
9788
b4c522fa
IB
9789
9790/++
9791 Returns the number of days from the current day of the week to the given
9792 day of the week. If they are the same, then the result is 0.
9793
9794 Params:
9795 currDoW = The current day of the week.
9796 dow = The day of the week to get the number of days to.
9797 +/
9798int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc
9799{
9800 if (currDoW == dow)
9801 return 0;
9802 if (currDoW < dow)
9803 return dow - currDoW;
9804 return DayOfWeek.sat - currDoW + dow + 1;
9805}
9806
9807///
9808@safe pure nothrow @nogc unittest
9809{
9810 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9811 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9812 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9813}
9814
9815@safe unittest
9816{
9817 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0);
9818 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1);
9819 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2);
9820 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3);
9821 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4);
9822 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5);
9823 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6);
9824
9825 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
9826 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
9827 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1);
9828 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
9829 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3);
9830 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4);
9831 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5);
9832
9833 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5);
9834 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6);
9835 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0);
9836 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1);
9837 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2);
9838 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3);
9839 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4);
9840
9841 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4);
9842 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5);
9843 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6);
9844 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0);
9845 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1);
9846 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2);
9847 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3);
9848
9849 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3);
9850 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4);
9851 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5);
9852 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6);
9853 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0);
9854 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1);
9855 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2);
9856
9857 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2);
9858 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3);
9859 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4);
9860 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5);
9861 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6);
9862 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0);
9863 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1);
9864
9865 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1);
9866 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2);
9867 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3);
9868 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4);
9869 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5);
9870 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6);
9871 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0);
9872}
9873
9874
9875/++
9876 Returns the number of months from the current months of the year to the
9877 given month of the year. If they are the same, then the result is 0.
9878
9879 Params:
9880 currMonth = The current month of the year.
9881 month = The month of the year to get the number of months to.
9882 +/
9883int monthsToMonth(int currMonth, int month) @safe pure
9884{
9885 enforceValid!"months"(currMonth);
9886 enforceValid!"months"(month);
9887
9888 if (currMonth == month)
9889 return 0;
9890 if (currMonth < month)
9891 return month - currMonth;
9892 return Month.dec - currMonth + month;
9893}
9894
9895///
9896@safe pure unittest
9897{
9898 assert(monthsToMonth(Month.jan, Month.jan) == 0);
9899 assert(monthsToMonth(Month.jan, Month.dec) == 11);
9900 assert(monthsToMonth(Month.jul, Month.oct) == 3);
9901}
9902
9903@safe unittest
9904{
9905 assert(monthsToMonth(Month.jan, Month.jan) == 0);
9906 assert(monthsToMonth(Month.jan, Month.feb) == 1);
9907 assert(monthsToMonth(Month.jan, Month.mar) == 2);
9908 assert(monthsToMonth(Month.jan, Month.apr) == 3);
9909 assert(monthsToMonth(Month.jan, Month.may) == 4);
9910 assert(monthsToMonth(Month.jan, Month.jun) == 5);
9911 assert(monthsToMonth(Month.jan, Month.jul) == 6);
9912 assert(monthsToMonth(Month.jan, Month.aug) == 7);
9913 assert(monthsToMonth(Month.jan, Month.sep) == 8);
9914 assert(monthsToMonth(Month.jan, Month.oct) == 9);
9915 assert(monthsToMonth(Month.jan, Month.nov) == 10);
9916 assert(monthsToMonth(Month.jan, Month.dec) == 11);
9917
9918 assert(monthsToMonth(Month.may, Month.jan) == 8);
9919 assert(monthsToMonth(Month.may, Month.feb) == 9);
9920 assert(monthsToMonth(Month.may, Month.mar) == 10);
9921 assert(monthsToMonth(Month.may, Month.apr) == 11);
9922 assert(monthsToMonth(Month.may, Month.may) == 0);
9923 assert(monthsToMonth(Month.may, Month.jun) == 1);
9924 assert(monthsToMonth(Month.may, Month.jul) == 2);
9925 assert(monthsToMonth(Month.may, Month.aug) == 3);
9926 assert(monthsToMonth(Month.may, Month.sep) == 4);
9927 assert(monthsToMonth(Month.may, Month.oct) == 5);
9928 assert(monthsToMonth(Month.may, Month.nov) == 6);
9929 assert(monthsToMonth(Month.may, Month.dec) == 7);
9930
9931 assert(monthsToMonth(Month.oct, Month.jan) == 3);
9932 assert(monthsToMonth(Month.oct, Month.feb) == 4);
9933 assert(monthsToMonth(Month.oct, Month.mar) == 5);
9934 assert(monthsToMonth(Month.oct, Month.apr) == 6);
9935 assert(monthsToMonth(Month.oct, Month.may) == 7);
9936 assert(monthsToMonth(Month.oct, Month.jun) == 8);
9937 assert(monthsToMonth(Month.oct, Month.jul) == 9);
9938 assert(monthsToMonth(Month.oct, Month.aug) == 10);
9939 assert(monthsToMonth(Month.oct, Month.sep) == 11);
9940 assert(monthsToMonth(Month.oct, Month.oct) == 0);
9941 assert(monthsToMonth(Month.oct, Month.nov) == 1);
9942 assert(monthsToMonth(Month.oct, Month.dec) == 2);
9943
9944 assert(monthsToMonth(Month.dec, Month.jan) == 1);
9945 assert(monthsToMonth(Month.dec, Month.feb) == 2);
9946 assert(monthsToMonth(Month.dec, Month.mar) == 3);
9947 assert(monthsToMonth(Month.dec, Month.apr) == 4);
9948 assert(monthsToMonth(Month.dec, Month.may) == 5);
9949 assert(monthsToMonth(Month.dec, Month.jun) == 6);
9950 assert(monthsToMonth(Month.dec, Month.jul) == 7);
9951 assert(monthsToMonth(Month.dec, Month.aug) == 8);
9952 assert(monthsToMonth(Month.dec, Month.sep) == 9);
9953 assert(monthsToMonth(Month.dec, Month.oct) == 10);
9954 assert(monthsToMonth(Month.dec, Month.nov) == 11);
9955 assert(monthsToMonth(Month.dec, Month.dec) == 0);
9956}
9957
9958
9959/++
9960 Whether the given Gregorian Year is a leap year.
9961
9962 Params:
9963 year = The year to to be tested.
9964 +/
9965bool yearIsLeapYear(int year) @safe pure nothrow @nogc
9966{
9967 if (year % 400 == 0)
9968 return true;
9969 if (year % 100 == 0)
9970 return false;
9971 return year % 4 == 0;
9972}
9973
9974///
9975@safe unittest
9976{
9977 foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
9978 {
9979 assert(!yearIsLeapYear(year));
9980 assert(!yearIsLeapYear(-year));
9981 }
9982
9983 foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
9984 {
9985 assert(yearIsLeapYear(year));
9986 assert(yearIsLeapYear(-year));
9987 }
9988}
9989
9990@safe unittest
9991{
9992 import std.format : format;
9993 foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999,
9994 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011])
9995 {
9996 assert(!yearIsLeapYear(year), format("year: %s.", year));
9997 assert(!yearIsLeapYear(-year), format("year: %s.", year));
9998 }
9999
10000 foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
10001 {
10002 assert(yearIsLeapYear(year), format("year: %s.", year));
10003 assert(yearIsLeapYear(-year), format("year: %s.", year));
10004 }
10005}
10006
10007
10008/++
10009 Whether the given type defines all of the necessary functions for it to
10010 function as a time point.
10011
5fee5ec3
IB
10012 1. `T` must define a static property named `min` which is the smallest
10013 value of `T` as `Unqual!T`.
b4c522fa 10014
5fee5ec3
IB
10015 2. `T` must define a static property named `max` which is the largest
10016 value of `T` as `Unqual!T`.
b4c522fa 10017
5fee5ec3
IB
10018 3. `T` must define an `opBinary` for addition and subtraction that
10019 accepts $(REF Duration, core,time) and returns `Unqual!T`.
b4c522fa 10020
5fee5ec3 10021 4. `T` must define an `opOpAssign` for addition and subtraction that
b4c522fa
IB
10022 accepts $(REF Duration, core,time) and returns $(D ref Unqual!T).
10023
5fee5ec3
IB
10024 5. `T` must define a `opBinary` for subtraction which accepts `T`
10025 and returns $(REF Duration, core,time).
b4c522fa
IB
10026 +/
10027template isTimePoint(T)
10028{
10029 import core.time : Duration;
10030 import std.traits : FunctionAttribute, functionAttributes, Unqual;
10031
10032 enum isTimePoint = hasMin &&
10033 hasMax &&
10034 hasOverloadedOpBinaryWithDuration &&
10035 hasOverloadedOpAssignWithDuration &&
10036 hasOverloadedOpBinaryWithSelf &&
10037 !is(U == Duration);
10038
10039private:
10040
10041 alias U = Unqual!T;
10042
10043 enum hasMin = __traits(hasMember, T, "min") &&
10044 is(typeof(T.min) == U) &&
10045 is(typeof({static assert(__traits(isStaticFunction, T.min));}));
10046
10047 enum hasMax = __traits(hasMember, T, "max") &&
10048 is(typeof(T.max) == U) &&
10049 is(typeof({static assert(__traits(isStaticFunction, T.max));}));
10050
10051 enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) &&
10052 is(typeof(T.init - Duration.init) == U);
10053
10054 enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) &&
10055 is(typeof(U.init -= Duration.init) == U) &&
10056 is(typeof(
10057 {
9fa27ed0
IB
10058 alias add = U.opOpAssign!"+";
10059 alias sub = U.opOpAssign!"-";
b4c522fa
IB
10060 alias FA = FunctionAttribute;
10061 static assert((functionAttributes!add & FA.ref_) != 0);
10062 static assert((functionAttributes!sub & FA.ref_) != 0);
10063 }));
10064
10065 enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration);
10066}
10067
10068///
10069@safe unittest
10070{
10071 import core.time : Duration;
10072 import std.datetime.interval : Interval;
10073 import std.datetime.systime : SysTime;
10074
10075 static assert(isTimePoint!Date);
10076 static assert(isTimePoint!DateTime);
10077 static assert(isTimePoint!SysTime);
10078 static assert(isTimePoint!TimeOfDay);
10079
10080 static assert(!isTimePoint!int);
10081 static assert(!isTimePoint!Duration);
10082 static assert(!isTimePoint!(Interval!SysTime));
10083}
10084
10085@safe unittest
10086{
10087 import core.time;
10088 import std.datetime.interval;
10089 import std.datetime.systime;
10090 import std.meta : AliasSeq;
10091
5fee5ec3 10092 static foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay))
b4c522fa
IB
10093 {
10094 static assert(isTimePoint!(const TP), TP.stringof);
10095 static assert(isTimePoint!(immutable TP), TP.stringof);
10096 }
10097
5fee5ec3 10098 static foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date))
b4c522fa
IB
10099 static assert(!isTimePoint!T, T.stringof);
10100}
10101
10102
10103/++
10104 Whether all of the given strings are valid units of time.
10105
5fee5ec3 10106 `"nsecs"` is not considered a valid unit of time. Nothing in std.datetime
b4c522fa
IB
10107 can handle precision greater than hnsecs, and the few functions in core.time
10108 which deal with "nsecs" deal with it explicitly.
10109 +/
10110bool validTimeUnits(string[] units...) @safe pure nothrow @nogc
10111{
10112 import std.algorithm.searching : canFind;
10113 foreach (str; units)
10114 {
10115 if (!canFind(timeStrings[], str))
10116 return false;
10117 }
10118 return true;
10119}
10120
10121///
10122@safe @nogc nothrow unittest
10123{
10124 assert(validTimeUnits("msecs", "seconds", "minutes"));
10125 assert(validTimeUnits("days", "weeks", "months"));
10126 assert(!validTimeUnits("ms", "seconds", "minutes"));
10127}
10128
10129
10130/++
5fee5ec3
IB
10131 Compares two time unit strings. `"years"` are the largest units and
10132 `"hnsecs"` are the smallest.
b4c522fa
IB
10133
10134 Returns:
10135 $(BOOKTABLE,
10136 $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10137 $(TR $(TD this == rhs) $(TD 0))
10138 $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10139 )
10140
10141 Throws:
10142 $(LREF DateTimeException) if either of the given strings is not a valid
10143 time unit string.
10144 +/
10145int cmpTimeUnits(string lhs, string rhs) @safe pure
10146{
10147 import std.algorithm.searching : countUntil;
10148 import std.exception : enforce;
10149 import std.format : format;
10150
5fee5ec3
IB
10151 immutable indexOfLHS = countUntil(timeStrings, lhs);
10152 immutable indexOfRHS = countUntil(timeStrings, rhs);
b4c522fa 10153
5fee5ec3
IB
10154 enforce!DateTimeException(indexOfLHS != -1, format("%s is not a valid TimeString", lhs));
10155 enforce!DateTimeException(indexOfRHS != -1, format("%s is not a valid TimeString", rhs));
b4c522fa
IB
10156
10157 if (indexOfLHS < indexOfRHS)
10158 return -1;
10159 if (indexOfLHS > indexOfRHS)
10160 return 1;
10161
10162 return 0;
10163}
10164
10165///
10166@safe pure unittest
10167{
5fee5ec3
IB
10168 import std.exception : assertThrown;
10169
b4c522fa
IB
10170 assert(cmpTimeUnits("hours", "hours") == 0);
10171 assert(cmpTimeUnits("hours", "weeks") < 0);
10172 assert(cmpTimeUnits("months", "seconds") > 0);
5fee5ec3
IB
10173
10174 assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
b4c522fa
IB
10175}
10176
10177@safe unittest
10178{
10179 foreach (i, outerUnits; timeStrings)
10180 {
10181 assert(cmpTimeUnits(outerUnits, outerUnits) == 0);
10182
10183 // For some reason, $ won't compile.
10184 foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length])
10185 assert(cmpTimeUnits(outerUnits, innerUnits) == -1);
10186 }
10187
10188 foreach (i, outerUnits; timeStrings)
10189 {
10190 foreach (innerUnits; timeStrings[0 .. i])
10191 assert(cmpTimeUnits(outerUnits, innerUnits) == 1);
10192 }
10193}
10194
10195
10196/++
5fee5ec3
IB
10197 Compares two time unit strings at compile time. `"years"` are the largest
10198 units and `"hnsecs"` are the smallest.
b4c522fa 10199
5fee5ec3
IB
10200 This template is used instead of `cmpTimeUnits` because exceptions
10201 can't be thrown at compile time and `cmpTimeUnits` must enforce that
b4c522fa
IB
10202 the strings it's given are valid time unit strings. This template uses a
10203 template constraint instead.
10204
10205 Returns:
10206 $(BOOKTABLE,
10207 $(TR $(TD this &lt; rhs) $(TD &lt; 0))
10208 $(TR $(TD this == rhs) $(TD 0))
10209 $(TR $(TD this &gt; rhs) $(TD &gt; 0))
10210 )
10211 +/
10212template CmpTimeUnits(string lhs, string rhs)
10213if (validTimeUnits(lhs, rhs))
10214{
10215 enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs);
10216}
10217
5fee5ec3
IB
10218///
10219@safe pure unittest
10220{
10221 static assert(CmpTimeUnits!("years", "weeks") > 0);
10222 static assert(CmpTimeUnits!("days", "days") == 0);
10223 static assert(CmpTimeUnits!("seconds", "hours") < 0);
10224}
b4c522fa
IB
10225
10226// Helper function for CmpTimeUnits.
10227private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow @nogc
10228{
10229 import std.algorithm.searching : countUntil;
10230 auto tstrings = timeStrings;
10231 immutable indexOfLHS = countUntil(tstrings, lhs);
10232 immutable indexOfRHS = countUntil(tstrings, rhs);
10233
10234 if (indexOfLHS < indexOfRHS)
10235 return -1;
10236 if (indexOfLHS > indexOfRHS)
10237 return 1;
10238
10239 return 0;
10240}
10241
10242@safe unittest
10243{
5fee5ec3 10244 static foreach (i; 0 .. timeStrings.length)
b4c522fa 10245 {
5fee5ec3 10246 static assert(CmpTimeUnits!(timeStrings[i], timeStrings[i]) == 0);
b4c522fa 10247
5fee5ec3
IB
10248 static foreach (next; timeStrings[i + 1 .. $])
10249 static assert(CmpTimeUnits!(timeStrings[i], next) == -1);
b4c522fa 10250
5fee5ec3
IB
10251 static foreach (prev; timeStrings[0 .. i])
10252 static assert(CmpTimeUnits!(timeStrings[i], prev) == 1);
b4c522fa 10253 }
b4c522fa
IB
10254}
10255
10256
10257package:
10258
10259
10260/+
10261 Array of the short (three letter) names of each month.
10262 +/
10263immutable string[12] _monthNames = ["Jan",
10264 "Feb",
10265 "Mar",
10266 "Apr",
10267 "May",
10268 "Jun",
10269 "Jul",
10270 "Aug",
10271 "Sep",
10272 "Oct",
10273 "Nov",
10274 "Dec"];
10275
10276/+
10277 The maximum valid Day in the given month in the given year.
10278
10279 Params:
10280 year = The year to get the day for.
10281 month = The month of the Gregorian Calendar to get the day for.
10282 +/
10283ubyte maxDay(int year, int month) @safe pure nothrow @nogc
10284in
10285{
10286 assert(valid!"months"(month));
10287}
5fee5ec3 10288do
b4c522fa
IB
10289{
10290 switch (month)
10291 {
10292 case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec:
10293 return 31;
10294 case Month.feb:
10295 return yearIsLeapYear(year) ? 29 : 28;
10296 case Month.apr, Month.jun, Month.sep, Month.nov:
10297 return 30;
10298 default:
10299 assert(0, "Invalid month.");
10300 }
10301}
10302
10303@safe unittest
10304{
10305 // Test A.D.
10306 assert(maxDay(1999, 1) == 31);
10307 assert(maxDay(1999, 2) == 28);
10308 assert(maxDay(1999, 3) == 31);
10309 assert(maxDay(1999, 4) == 30);
10310 assert(maxDay(1999, 5) == 31);
10311 assert(maxDay(1999, 6) == 30);
10312 assert(maxDay(1999, 7) == 31);
10313 assert(maxDay(1999, 8) == 31);
10314 assert(maxDay(1999, 9) == 30);
10315 assert(maxDay(1999, 10) == 31);
10316 assert(maxDay(1999, 11) == 30);
10317 assert(maxDay(1999, 12) == 31);
10318
10319 assert(maxDay(2000, 1) == 31);
10320 assert(maxDay(2000, 2) == 29);
10321 assert(maxDay(2000, 3) == 31);
10322 assert(maxDay(2000, 4) == 30);
10323 assert(maxDay(2000, 5) == 31);
10324 assert(maxDay(2000, 6) == 30);
10325 assert(maxDay(2000, 7) == 31);
10326 assert(maxDay(2000, 8) == 31);
10327 assert(maxDay(2000, 9) == 30);
10328 assert(maxDay(2000, 10) == 31);
10329 assert(maxDay(2000, 11) == 30);
10330 assert(maxDay(2000, 12) == 31);
10331
10332 // Test B.C.
10333 assert(maxDay(-1999, 1) == 31);
10334 assert(maxDay(-1999, 2) == 28);
10335 assert(maxDay(-1999, 3) == 31);
10336 assert(maxDay(-1999, 4) == 30);
10337 assert(maxDay(-1999, 5) == 31);
10338 assert(maxDay(-1999, 6) == 30);
10339 assert(maxDay(-1999, 7) == 31);
10340 assert(maxDay(-1999, 8) == 31);
10341 assert(maxDay(-1999, 9) == 30);
10342 assert(maxDay(-1999, 10) == 31);
10343 assert(maxDay(-1999, 11) == 30);
10344 assert(maxDay(-1999, 12) == 31);
10345
10346 assert(maxDay(-2000, 1) == 31);
10347 assert(maxDay(-2000, 2) == 29);
10348 assert(maxDay(-2000, 3) == 31);
10349 assert(maxDay(-2000, 4) == 30);
10350 assert(maxDay(-2000, 5) == 31);
10351 assert(maxDay(-2000, 6) == 30);
10352 assert(maxDay(-2000, 7) == 31);
10353 assert(maxDay(-2000, 8) == 31);
10354 assert(maxDay(-2000, 9) == 30);
10355 assert(maxDay(-2000, 10) == 31);
10356 assert(maxDay(-2000, 11) == 30);
10357 assert(maxDay(-2000, 12) == 31);
10358}
10359
10360/+
10361 Splits out a particular unit from hnsecs and gives the value for that
10362 unit and the remaining hnsecs. It really shouldn't be used unless unless
10363 all units larger than the given units have already been split out.
10364
10365 Params:
10366 units = The units to split out.
10367 hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left
10368 after splitting out the given units.
10369
10370 Returns:
10371 The number of the given units from converting hnsecs to those units.
10372 +/
10373long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow @nogc
10374if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0)
10375{
10376 import core.time : convert;
10377 immutable value = convert!("hnsecs", units)(hnsecs);
10378 hnsecs -= convert!(units, "hnsecs")(value);
10379 return value;
10380}
10381
10382@safe unittest
10383{
10384 auto hnsecs = 2595000000007L;
10385 immutable days = splitUnitsFromHNSecs!"days"(hnsecs);
10386 assert(days == 3);
10387 assert(hnsecs == 3000000007);
10388
10389 immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs);
10390 assert(minutes == 5);
10391 assert(hnsecs == 7);
10392}
10393
10394
10395/+
10396 Returns the day of the week for the given day of the Gregorian Calendar.
10397
10398 Params:
10399 day = The day of the Gregorian Calendar for which to get the day of
10400 the week.
10401 +/
10402DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc
10403{
10404 // January 1st, 1 A.D. was a Monday
10405 if (day >= 0)
10406 return cast(DayOfWeek)(day % 7);
10407 else
10408 {
10409 immutable dow = cast(DayOfWeek)((day % 7) + 7);
10410
10411 if (dow == 7)
10412 return DayOfWeek.sun;
10413 else
10414 return dow;
10415 }
10416}
10417
10418@safe unittest
10419{
10420 import std.datetime.systime : SysTime;
10421
10422 // Test A.D.
10423 assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon);
10424 assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue);
10425 assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed);
10426 assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu);
10427 assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri);
10428 assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat);
10429 assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun);
10430 assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon);
10431 assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue);
10432 assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue);
10433 assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed);
10434 assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu);
10435 assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10436 assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat);
10437 assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun);
10438 assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon);
10439 assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue);
10440 assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed);
10441 assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu);
10442 assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri);
10443 assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat);
10444 assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun);
10445
10446 // Test B.C.
10447 assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun);
10448 assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat);
10449 assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri);
10450 assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu);
10451 assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed);
10452 assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue);
10453 assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon);
10454 assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun);
10455 assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat);
10456}
10457
10458
10459private:
10460
10461enum daysInYear = 365; // The number of days in a non-leap year.
10462enum daysInLeapYear = 366; // The numbef or days in a leap year.
10463enum daysIn4Years = daysInYear * 3 + daysInLeapYear; // Number of days in 4 years.
10464enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years.
10465enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years.
10466
10467/+
10468 Array of integers representing the last days of each month in a year.
10469 +/
10470immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
10471
10472/+
10473 Array of integers representing the last days of each month in a leap year.
10474 +/
10475immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
10476
10477
10478/+
10479 Returns the string representation of the given month.
10480 +/
10481string monthToString(Month month) @safe pure
10482{
10483 import std.format : format;
10484 assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month));
10485 return _monthNames[month - Month.jan];
10486}
10487
10488@safe unittest
10489{
10490 assert(monthToString(Month.jan) == "Jan");
10491 assert(monthToString(Month.feb) == "Feb");
10492 assert(monthToString(Month.mar) == "Mar");
10493 assert(monthToString(Month.apr) == "Apr");
10494 assert(monthToString(Month.may) == "May");
10495 assert(monthToString(Month.jun) == "Jun");
10496 assert(monthToString(Month.jul) == "Jul");
10497 assert(monthToString(Month.aug) == "Aug");
10498 assert(monthToString(Month.sep) == "Sep");
10499 assert(monthToString(Month.oct) == "Oct");
10500 assert(monthToString(Month.nov) == "Nov");
10501 assert(monthToString(Month.dec) == "Dec");
10502}
10503
10504
10505/+
10506 Returns the Month corresponding to the given string.
10507
10508 Params:
10509 monthStr = The string representation of the month to get the Month for.
10510
10511 Throws:
10512 $(REF DateTimeException,std,datetime,date) if the given month is not a
10513 valid month string.
10514 +/
5fee5ec3
IB
10515Month monthFromString(T)(T monthStr) @safe pure
10516if (isSomeString!T)
b4c522fa
IB
10517{
10518 import std.format : format;
10519 switch (monthStr)
10520 {
10521 case "Jan":
10522 return Month.jan;
10523 case "Feb":
10524 return Month.feb;
10525 case "Mar":
10526 return Month.mar;
10527 case "Apr":
10528 return Month.apr;
10529 case "May":
10530 return Month.may;
10531 case "Jun":
10532 return Month.jun;
10533 case "Jul":
10534 return Month.jul;
10535 case "Aug":
10536 return Month.aug;
10537 case "Sep":
10538 return Month.sep;
10539 case "Oct":
10540 return Month.oct;
10541 case "Nov":
10542 return Month.nov;
10543 case "Dec":
10544 return Month.dec;
10545 default:
5fee5ec3 10546 throw new DateTimeException(format!"Invalid month %s"(monthStr));
b4c522fa
IB
10547 }
10548}
10549
10550@safe unittest
10551{
5fee5ec3 10552 import std.conv : to;
b4c522fa
IB
10553 import std.traits : EnumMembers;
10554 foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY",
10555 "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"])
10556 {
5fee5ec3 10557 assertThrown!DateTimeException(monthFromString(badStr), badStr);
b4c522fa
IB
10558 }
10559
10560 foreach (month; EnumMembers!Month)
10561 {
5fee5ec3 10562 assert(monthFromString(monthToString(month)) == month, month.to!string);
b4c522fa
IB
10563 }
10564}
10565
10566
5fee5ec3
IB
10567// NOTE: all the non-simple array literals are wrapped in functions, because
10568// otherwise importing causes re-evaluation of the static initializers using
10569// CTFE with unittests enabled
10570version (StdUnittest)
b4c522fa 10571{
5fee5ec3 10572private @safe:
b4c522fa
IB
10573 // All of these helper arrays are sorted in ascending order.
10574 auto testYearsBC = [-1999, -1200, -600, -4, -1, 0];
10575 auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012];
10576
10577 // I'd use a Tuple, but I get forward reference errors if I try.
10578 struct MonthDay
10579 {
10580 Month month;
10581 short day;
10582
10583 this(int m, short d)
10584 {
10585 month = cast(Month) m;
10586 day = d;
10587 }
10588 }
10589
5fee5ec3
IB
10590 MonthDay[] testMonthDays()
10591 {
10592 static MonthDay[] result = [MonthDay(1, 1),
b4c522fa
IB
10593 MonthDay(1, 2),
10594 MonthDay(3, 17),
10595 MonthDay(7, 4),
10596 MonthDay(10, 27),
10597 MonthDay(12, 30),
10598 MonthDay(12, 31)];
5fee5ec3
IB
10599 return result;
10600 }
b4c522fa
IB
10601
10602 auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31];
10603
5fee5ec3
IB
10604 TimeOfDay[] testTODs()
10605 {
10606 static result = [TimeOfDay(0, 0, 0),
b4c522fa
IB
10607 TimeOfDay(0, 0, 1),
10608 TimeOfDay(0, 1, 0),
10609 TimeOfDay(1, 0, 0),
10610 TimeOfDay(13, 13, 13),
10611 TimeOfDay(23, 59, 59)];
5fee5ec3
IB
10612 return result;
10613 }
b4c522fa
IB
10614
10615 auto testHours = [0, 1, 12, 22, 23];
10616 auto testMinSecs = [0, 1, 30, 58, 59];
10617
10618 // Throwing exceptions is incredibly expensive, so we want to use a smaller
10619 // set of values for tests using assertThrown.
5fee5ec3
IB
10620 TimeOfDay[] testTODsThrown()
10621 {
10622 static result = [TimeOfDay(0, 0, 0),
b4c522fa
IB
10623 TimeOfDay(13, 13, 13),
10624 TimeOfDay(23, 59, 59)];
5fee5ec3
IB
10625 return result;
10626 }
b4c522fa
IB
10627
10628 Date[] testDatesBC;
10629 Date[] testDatesAD;
10630
10631 DateTime[] testDateTimesBC;
10632 DateTime[] testDateTimesAD;
10633
10634 // I'd use a Tuple, but I get forward reference errors if I try.
10635 struct GregDay { int day; Date date; }
5fee5ec3
IB
10636 GregDay[] testGregDaysBC()
10637 {
10638 static result = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar
b4c522fa
IB
10639 GregDay(-735_233, Date(-2012, 1, 1)),
10640 GregDay(-735_202, Date(-2012, 2, 1)),
10641 GregDay(-735_175, Date(-2012, 2, 28)),
10642 GregDay(-735_174, Date(-2012, 2, 29)),
10643 GregDay(-735_173, Date(-2012, 3, 1)),
10644 GregDay(-734_502, Date(-2010, 1, 1)),
10645 GregDay(-734_472, Date(-2010, 1, 31)),
10646 GregDay(-734_471, Date(-2010, 2, 1)),
10647 GregDay(-734_444, Date(-2010, 2, 28)),
10648 GregDay(-734_443, Date(-2010, 3, 1)),
10649 GregDay(-734_413, Date(-2010, 3, 31)),
10650 GregDay(-734_412, Date(-2010, 4, 1)),
10651 GregDay(-734_383, Date(-2010, 4, 30)),
10652 GregDay(-734_382, Date(-2010, 5, 1)),
10653 GregDay(-734_352, Date(-2010, 5, 31)),
10654 GregDay(-734_351, Date(-2010, 6, 1)),
10655 GregDay(-734_322, Date(-2010, 6, 30)),
10656 GregDay(-734_321, Date(-2010, 7, 1)),
10657 GregDay(-734_291, Date(-2010, 7, 31)),
10658 GregDay(-734_290, Date(-2010, 8, 1)),
10659 GregDay(-734_260, Date(-2010, 8, 31)),
10660 GregDay(-734_259, Date(-2010, 9, 1)),
10661 GregDay(-734_230, Date(-2010, 9, 30)),
10662 GregDay(-734_229, Date(-2010, 10, 1)),
10663 GregDay(-734_199, Date(-2010, 10, 31)),
10664 GregDay(-734_198, Date(-2010, 11, 1)),
10665 GregDay(-734_169, Date(-2010, 11, 30)),
10666 GregDay(-734_168, Date(-2010, 12, 1)),
10667 GregDay(-734_139, Date(-2010, 12, 30)),
10668 GregDay(-734_138, Date(-2010, 12, 31)),
10669 GregDay(-731_215, Date(-2001, 1, 1)),
10670 GregDay(-730_850, Date(-2000, 1, 1)),
10671 GregDay(-730_849, Date(-2000, 1, 2)),
10672 GregDay(-730_486, Date(-2000, 12, 30)),
10673 GregDay(-730_485, Date(-2000, 12, 31)),
10674 GregDay(-730_484, Date(-1999, 1, 1)),
10675 GregDay(-694_690, Date(-1901, 1, 1)),
10676 GregDay(-694_325, Date(-1900, 1, 1)),
10677 GregDay(-585_118, Date(-1601, 1, 1)),
10678 GregDay(-584_753, Date(-1600, 1, 1)),
10679 GregDay(-584_388, Date(-1600, 12, 31)),
10680 GregDay(-584_387, Date(-1599, 1, 1)),
10681 GregDay(-365_972, Date(-1001, 1, 1)),
10682 GregDay(-365_607, Date(-1000, 1, 1)),
10683 GregDay(-183_351, Date(-501, 1, 1)),
10684 GregDay(-182_986, Date(-500, 1, 1)),
10685 GregDay(-182_621, Date(-499, 1, 1)),
10686 GregDay(-146_827, Date(-401, 1, 1)),
10687 GregDay(-146_462, Date(-400, 1, 1)),
10688 GregDay(-146_097, Date(-400, 12, 31)),
10689 GregDay(-110_302, Date(-301, 1, 1)),
10690 GregDay(-109_937, Date(-300, 1, 1)),
10691 GregDay(-73_778, Date(-201, 1, 1)),
10692 GregDay(-73_413, Date(-200, 1, 1)),
10693 GregDay(-38_715, Date(-105, 1, 1)),
10694 GregDay(-37_254, Date(-101, 1, 1)),
10695 GregDay(-36_889, Date(-100, 1, 1)),
10696 GregDay(-36_524, Date(-99, 1, 1)),
10697 GregDay(-36_160, Date(-99, 12, 31)),
10698 GregDay(-35_794, Date(-97, 1, 1)),
10699 GregDay(-18_627, Date(-50, 1, 1)),
10700 GregDay(-18_262, Date(-49, 1, 1)),
10701 GregDay(-3652, Date(-9, 1, 1)),
10702 GregDay(-2191, Date(-5, 1, 1)),
10703 GregDay(-1827, Date(-5, 12, 31)),
10704 GregDay(-1826, Date(-4, 1, 1)),
10705 GregDay(-1825, Date(-4, 1, 2)),
10706 GregDay(-1462, Date(-4, 12, 30)),
10707 GregDay(-1461, Date(-4, 12, 31)),
10708 GregDay(-1460, Date(-3, 1, 1)),
10709 GregDay(-1096, Date(-3, 12, 31)),
10710 GregDay(-1095, Date(-2, 1, 1)),
10711 GregDay(-731, Date(-2, 12, 31)),
10712 GregDay(-730, Date(-1, 1, 1)),
10713 GregDay(-367, Date(-1, 12, 30)),
10714 GregDay(-366, Date(-1, 12, 31)),
10715 GregDay(-365, Date(0, 1, 1)),
10716 GregDay(-31, Date(0, 11, 30)),
10717 GregDay(-30, Date(0, 12, 1)),
10718 GregDay(-1, Date(0, 12, 30)),
10719 GregDay(0, Date(0, 12, 31))];
5fee5ec3
IB
10720 return result;
10721 }
b4c522fa 10722
5fee5ec3
IB
10723 GregDay[] testGregDaysAD()
10724 {
10725 static result = [GregDay(1, Date(1, 1, 1)),
b4c522fa
IB
10726 GregDay(2, Date(1, 1, 2)),
10727 GregDay(32, Date(1, 2, 1)),
10728 GregDay(365, Date(1, 12, 31)),
10729 GregDay(366, Date(2, 1, 1)),
10730 GregDay(731, Date(3, 1, 1)),
10731 GregDay(1096, Date(4, 1, 1)),
10732 GregDay(1097, Date(4, 1, 2)),
10733 GregDay(1460, Date(4, 12, 30)),
10734 GregDay(1461, Date(4, 12, 31)),
10735 GregDay(1462, Date(5, 1, 1)),
10736 GregDay(17_898, Date(50, 1, 1)),
10737 GregDay(35_065, Date(97, 1, 1)),
10738 GregDay(36_160, Date(100, 1, 1)),
10739 GregDay(36_525, Date(101, 1, 1)),
10740 GregDay(37_986, Date(105, 1, 1)),
10741 GregDay(72_684, Date(200, 1, 1)),
10742 GregDay(73_049, Date(201, 1, 1)),
10743 GregDay(109_208, Date(300, 1, 1)),
10744 GregDay(109_573, Date(301, 1, 1)),
10745 GregDay(145_732, Date(400, 1, 1)),
10746 GregDay(146_098, Date(401, 1, 1)),
10747 GregDay(182_257, Date(500, 1, 1)),
10748 GregDay(182_622, Date(501, 1, 1)),
10749 GregDay(364_878, Date(1000, 1, 1)),
10750 GregDay(365_243, Date(1001, 1, 1)),
10751 GregDay(584_023, Date(1600, 1, 1)),
10752 GregDay(584_389, Date(1601, 1, 1)),
10753 GregDay(693_596, Date(1900, 1, 1)),
10754 GregDay(693_961, Date(1901, 1, 1)),
10755 GregDay(729_755, Date(1999, 1, 1)),
10756 GregDay(730_120, Date(2000, 1, 1)),
10757 GregDay(730_121, Date(2000, 1, 2)),
10758 GregDay(730_484, Date(2000, 12, 30)),
10759 GregDay(730_485, Date(2000, 12, 31)),
10760 GregDay(730_486, Date(2001, 1, 1)),
10761 GregDay(733_773, Date(2010, 1, 1)),
10762 GregDay(733_774, Date(2010, 1, 2)),
10763 GregDay(733_803, Date(2010, 1, 31)),
10764 GregDay(733_804, Date(2010, 2, 1)),
10765 GregDay(733_831, Date(2010, 2, 28)),
10766 GregDay(733_832, Date(2010, 3, 1)),
10767 GregDay(733_862, Date(2010, 3, 31)),
10768 GregDay(733_863, Date(2010, 4, 1)),
10769 GregDay(733_892, Date(2010, 4, 30)),
10770 GregDay(733_893, Date(2010, 5, 1)),
10771 GregDay(733_923, Date(2010, 5, 31)),
10772 GregDay(733_924, Date(2010, 6, 1)),
10773 GregDay(733_953, Date(2010, 6, 30)),
10774 GregDay(733_954, Date(2010, 7, 1)),
10775 GregDay(733_984, Date(2010, 7, 31)),
10776 GregDay(733_985, Date(2010, 8, 1)),
10777 GregDay(734_015, Date(2010, 8, 31)),
10778 GregDay(734_016, Date(2010, 9, 1)),
10779 GregDay(734_045, Date(2010, 9, 30)),
10780 GregDay(734_046, Date(2010, 10, 1)),
10781 GregDay(734_076, Date(2010, 10, 31)),
10782 GregDay(734_077, Date(2010, 11, 1)),
10783 GregDay(734_106, Date(2010, 11, 30)),
10784 GregDay(734_107, Date(2010, 12, 1)),
10785 GregDay(734_136, Date(2010, 12, 30)),
10786 GregDay(734_137, Date(2010, 12, 31)),
10787 GregDay(734_503, Date(2012, 1, 1)),
10788 GregDay(734_534, Date(2012, 2, 1)),
10789 GregDay(734_561, Date(2012, 2, 28)),
10790 GregDay(734_562, Date(2012, 2, 29)),
10791 GregDay(734_563, Date(2012, 3, 1)),
10792 GregDay(734_858, Date(2012, 12, 21))];
5fee5ec3
IB
10793 return result;
10794 }
b4c522fa
IB
10795
10796 // I'd use a Tuple, but I get forward reference errors if I try.
10797 struct DayOfYear { int day; MonthDay md; }
5fee5ec3
IB
10798 DayOfYear[] testDaysOfYear()
10799 {
10800 static result = [DayOfYear(1, MonthDay(1, 1)),
b4c522fa
IB
10801 DayOfYear(2, MonthDay(1, 2)),
10802 DayOfYear(3, MonthDay(1, 3)),
10803 DayOfYear(31, MonthDay(1, 31)),
10804 DayOfYear(32, MonthDay(2, 1)),
10805 DayOfYear(59, MonthDay(2, 28)),
10806 DayOfYear(60, MonthDay(3, 1)),
10807 DayOfYear(90, MonthDay(3, 31)),
10808 DayOfYear(91, MonthDay(4, 1)),
10809 DayOfYear(120, MonthDay(4, 30)),
10810 DayOfYear(121, MonthDay(5, 1)),
10811 DayOfYear(151, MonthDay(5, 31)),
10812 DayOfYear(152, MonthDay(6, 1)),
10813 DayOfYear(181, MonthDay(6, 30)),
10814 DayOfYear(182, MonthDay(7, 1)),
10815 DayOfYear(212, MonthDay(7, 31)),
10816 DayOfYear(213, MonthDay(8, 1)),
10817 DayOfYear(243, MonthDay(8, 31)),
10818 DayOfYear(244, MonthDay(9, 1)),
10819 DayOfYear(273, MonthDay(9, 30)),
10820 DayOfYear(274, MonthDay(10, 1)),
10821 DayOfYear(304, MonthDay(10, 31)),
10822 DayOfYear(305, MonthDay(11, 1)),
10823 DayOfYear(334, MonthDay(11, 30)),
10824 DayOfYear(335, MonthDay(12, 1)),
10825 DayOfYear(363, MonthDay(12, 29)),
10826 DayOfYear(364, MonthDay(12, 30)),
10827 DayOfYear(365, MonthDay(12, 31))];
5fee5ec3
IB
10828 return result;
10829 }
b4c522fa 10830
5fee5ec3
IB
10831 DayOfYear[] testDaysOfLeapYear()
10832 {
10833 static result = [DayOfYear(1, MonthDay(1, 1)),
b4c522fa
IB
10834 DayOfYear(2, MonthDay(1, 2)),
10835 DayOfYear(3, MonthDay(1, 3)),
10836 DayOfYear(31, MonthDay(1, 31)),
10837 DayOfYear(32, MonthDay(2, 1)),
10838 DayOfYear(59, MonthDay(2, 28)),
10839 DayOfYear(60, MonthDay(2, 29)),
10840 DayOfYear(61, MonthDay(3, 1)),
10841 DayOfYear(91, MonthDay(3, 31)),
10842 DayOfYear(92, MonthDay(4, 1)),
10843 DayOfYear(121, MonthDay(4, 30)),
10844 DayOfYear(122, MonthDay(5, 1)),
10845 DayOfYear(152, MonthDay(5, 31)),
10846 DayOfYear(153, MonthDay(6, 1)),
10847 DayOfYear(182, MonthDay(6, 30)),
10848 DayOfYear(183, MonthDay(7, 1)),
10849 DayOfYear(213, MonthDay(7, 31)),
10850 DayOfYear(214, MonthDay(8, 1)),
10851 DayOfYear(244, MonthDay(8, 31)),
10852 DayOfYear(245, MonthDay(9, 1)),
10853 DayOfYear(274, MonthDay(9, 30)),
10854 DayOfYear(275, MonthDay(10, 1)),
10855 DayOfYear(305, MonthDay(10, 31)),
10856 DayOfYear(306, MonthDay(11, 1)),
10857 DayOfYear(335, MonthDay(11, 30)),
10858 DayOfYear(336, MonthDay(12, 1)),
10859 DayOfYear(364, MonthDay(12, 29)),
10860 DayOfYear(365, MonthDay(12, 30)),
10861 DayOfYear(366, MonthDay(12, 31))];
5fee5ec3
IB
10862 return result;
10863 }
b4c522fa 10864
5fee5ec3 10865 void initializeTests()
b4c522fa
IB
10866 {
10867 foreach (year; testYearsBC)
10868 {
10869 foreach (md; testMonthDays)
10870 testDatesBC ~= Date(year, md.month, md.day);
10871 }
10872
10873 foreach (year; testYearsAD)
10874 {
10875 foreach (md; testMonthDays)
10876 testDatesAD ~= Date(year, md.month, md.day);
10877 }
10878
10879 foreach (dt; testDatesBC)
10880 {
10881 foreach (tod; testTODs)
10882 testDateTimesBC ~= DateTime(dt, tod);
10883 }
10884
10885 foreach (dt; testDatesAD)
10886 {
10887 foreach (tod; testTODs)
10888 testDateTimesAD ~= DateTime(dt, tod);
10889 }
10890 }
10891}