1 /++ 2 Fast BetterC Date type with Boost ABI and mangling compatability. 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 )) 11 $(TR $(TD Other date types) $(TD 12 $(LREF Month) 13 $(LREF DayOfWeek) 14 )) 15 $(TR $(TD Date checking) $(TD 16 $(LREF valid) 17 $(LREF yearIsLeapYear) 18 )) 19 $(TR $(TD Date conversion) $(TD 20 $(LREF daysToDayOfWeek) 21 )) 22 $(TR $(TD Other) $(TD 23 $(LREF AllowDayOverflow) 24 $(LREF DateTimeException) 25 )) 26 )) 27 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 28 Authors: $(HTTP jmdavisprog.com, Jonathan M Davis), Ilya Yaroshenko (boost-like and BetterC rework) 29 +/ 30 module mir.date; 31 32 import mir.primitives: isOutputRange; 33 import mir.serde: serdeProxy; 34 import mir.timestamp: Timestamp; 35 import std.traits: isSomeChar, Unqual; 36 37 version(mir_test) 38 version(D_Exceptions) 39 version(unittest) import std.exception : assertThrown; 40 41 version(test_with_asdf) 42 unittest 43 { 44 import asdf.serialization; 45 46 assert(Date(2020, 3, 19).serializeToJson == `"2020-03-19"`); 47 assert(`"2020-03-19"`.deserialize!Date == Date(2020, 3, 19)); 48 assert(`"20200319"`.deserialize!Date == Date(2020, 3, 19)); 49 assert(`"2020-Mar-19"`.deserialize!Date == Date(2020, 3, 19)); 50 } 51 52 /++ 53 Returns whether the given value is valid for the given unit type when in a 54 time point. Naturally, a duration is not held to a particular range, but 55 the values in a time point are (e.g. a month must be in the range of 56 1 - 12 inclusive). 57 Params: 58 units = The units of time to validate. 59 value = The number to validate. 60 +/ 61 bool valid(string units)(int value) @safe pure nothrow @nogc 62 if (units == "months" || 63 units == "hours" || 64 units == "minutes" || 65 units == "seconds") 66 { 67 static if (units == "months") 68 return value >= Month.jan && value <= Month.dec; 69 else static if (units == "hours") 70 return value >= 0 && value <= 23; 71 else static if (units == "minutes") 72 return value >= 0 && value <= 59; 73 else static if (units == "seconds") 74 return value >= 0 && value <= 59; 75 } 76 77 /// 78 version (mir_test) 79 @safe unittest 80 { 81 assert(valid!"hours"(12)); 82 assert(!valid!"hours"(32)); 83 assert(valid!"months"(12)); 84 assert(!valid!"months"(13)); 85 } 86 87 /++ 88 Returns whether the given day is valid for the given year and month. 89 Params: 90 units = The units of time to validate. 91 year = The year of the day to validate. 92 month = The month of the day to validate (January is 1). 93 day = The day to validate. 94 +/ 95 bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc 96 if (units == "days") 97 { 98 return day > 0 && day <= maxDay(year, month); 99 } 100 101 /// 102 version (mir_test) 103 @safe pure nothrow @nogc unittest 104 { 105 assert(valid!"days"(2016, 2, 29)); 106 assert(!valid!"days"(2016, 2, 30)); 107 assert(valid!"days"(2017, 2, 20)); 108 assert(!valid!"days"(2017, 2, 29)); 109 } 110 111 /// 112 enum AllowDayOverflow : bool 113 { 114 /// 115 no, 116 /// 117 yes 118 } 119 120 /++ 121 Whether the given Gregorian Year is a leap year. 122 Params: 123 year = The year to to be tested. 124 +/ 125 bool yearIsLeapYear(int year) @safe pure nothrow @nogc 126 { 127 if (year % 400 == 0) 128 return true; 129 if (year % 100 == 0) 130 return false; 131 return year % 4 == 0; 132 } 133 134 /// 135 version (mir_test) 136 @safe unittest 137 { 138 foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010]) 139 { 140 assert(!yearIsLeapYear(year)); 141 assert(!yearIsLeapYear(-year)); 142 } 143 144 foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) 145 { 146 assert(yearIsLeapYear(year)); 147 assert(yearIsLeapYear(-year)); 148 } 149 } 150 151 version (mir_test) 152 @safe unittest 153 { 154 import std.format : format; 155 foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, 156 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) 157 { 158 assert(!yearIsLeapYear(year), format("year: %s.", year)); 159 assert(!yearIsLeapYear(-year), format("year: %s.", year)); 160 } 161 162 foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) 163 { 164 assert(yearIsLeapYear(year), format("year: %s.", year)); 165 assert(yearIsLeapYear(-year), format("year: %s.", year)); 166 } 167 } 168 169 /// 170 enum Month : short 171 { 172 /// 173 jan = 1, 174 /// 175 feb, 176 /// 177 mar, 178 /// 179 apr, 180 /// 181 may, 182 /// 183 jun, 184 /// 185 jul, 186 /// 187 aug, 188 /// 189 sep, 190 /// 191 oct, 192 /// 193 nov, 194 /// 195 dec, 196 } 197 198 version(D_Exceptions) 199 /// 200 class DateTimeException : Exception 201 { 202 /// 203 @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 204 { 205 super(msg, file, line, nextInChain); 206 } 207 208 /// ditto 209 @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 210 { 211 super(msg, file, line, nextInChain); 212 } 213 } 214 215 version(D_Exceptions) 216 { 217 private static immutable InvalidMonth = new DateTimeException("Invalid Month"); 218 private static immutable InvalidDay = new DateTimeException("Invalid Day"); 219 private static immutable InvalidISOString = new DateTimeException("Invalid ISO String"); 220 private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String"); 221 private static immutable InvalidSimpleString = new DateTimeException("Invalid Simple String"); 222 private static immutable InvalidString = new DateTimeException("Invalid String"); 223 } 224 225 version (mir_test) 226 @safe unittest 227 { 228 initializeTests(); 229 } 230 231 /++ 232 Represents the 7 days of the Gregorian week (Monday is 0). 233 +/ 234 extern(C++, "mir") 235 enum DayOfWeek 236 { 237 mon = 0, /// 238 tue, /// 239 wed, /// 240 thu, /// 241 fri, /// 242 sat, /// 243 sun, /// 244 } 245 246 /// 247 @serdeProxy!Timestamp 248 struct YearMonthDay 249 { 250 short year = 1; 251 Month month = Month.jan; 252 ubyte day = 1; 253 254 /// 255 Timestamp timestamp() @safe pure nothrow @nogc @property 256 { 257 return Timestamp(year, cast(ubyte)month, day); 258 } 259 260 /// 261 alias opCast(T : Timestamp) = timestamp; 262 263 /// 264 version(mir_test) 265 unittest 266 { 267 import mir.timestamp; 268 auto timestamp = cast(Timestamp) YearMonthDay(2020, Month.may, 12); 269 } 270 271 /// 272 this(short year, Month month, ubyte day) @safe pure nothrow @nogc 273 { 274 this.year = year; 275 this.month = month; 276 this.day = day; 277 } 278 279 /// 280 this(Date date) @safe pure nothrow @nogc 281 { 282 this = date.yearMonthDay; 283 } 284 285 version(D_Exceptions) 286 /// 287 this(Timestamp timestamp) @safe pure nothrow @nogc 288 { 289 if (timestamp.precision != Timestamp.Precision.day) 290 { 291 static immutable exc = new Exception("YearMonthDay: invalid timestamp precision"); 292 } 293 with(timestamp) this(year, cast(Month)month, day); 294 } 295 296 // Shares documentation with "years" version. 297 @safe pure nothrow @nogc 298 ref YearMonthDay add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) 299 if (units == "months") 300 { 301 auto years = months / 12; 302 months %= 12; 303 auto newMonth = month + months; 304 305 if (months < 0) 306 { 307 if (newMonth < 1) 308 { 309 newMonth += 12; 310 --years; 311 } 312 } 313 else if (newMonth > 12) 314 { 315 newMonth -= 12; 316 ++years; 317 } 318 319 year += years; 320 month = cast(Month) newMonth; 321 322 immutable currMaxDay = maxDay(year, month); 323 immutable overflow = day - currMaxDay; 324 325 if (overflow > 0) 326 { 327 if (allowOverflow == AllowDayOverflow.yes) 328 { 329 ++month; 330 day = cast(ubyte) overflow; 331 } 332 else 333 day = cast(ubyte) currMaxDay; 334 } 335 336 return this; 337 } 338 339 // Shares documentation with "years" version. 340 @safe pure nothrow @nogc 341 ref YearMonthDay add(string units)(long years, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) 342 if (units == "years") 343 { 344 year += years; 345 346 immutable currMaxDay = maxDay(year, month); 347 immutable overflow = day - currMaxDay; 348 349 if (overflow > 0) 350 { 351 if (allowOverflow == AllowDayOverflow.yes) 352 { 353 ++month; 354 day = cast(ubyte) overflow; 355 } 356 else 357 day = cast(ubyte) currMaxDay; 358 } 359 return this; 360 } 361 362 /++ 363 Day of the year this $(LREF Date) is on. 364 +/ 365 @property int dayOfYear() const @safe pure nothrow @nogc 366 { 367 if (month >= Month.jan && month <= Month.dec) 368 { 369 immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; 370 auto monthIndex = month - Month.jan; 371 372 return lastDay[monthIndex] + day; 373 } 374 assert(0, "Invalid month."); 375 } 376 377 /// 378 version (mir_test) 379 @safe unittest 380 { 381 assert(YearMonthDay(1999, cast(Month) 1, 1).dayOfYear == 1); 382 assert(YearMonthDay(1999, cast(Month) 12, 31).dayOfYear == 365); 383 assert(YearMonthDay(2000, cast(Month) 12, 31).dayOfYear == 366); 384 } 385 386 /++ 387 Whether this $(LREF Date) is in a leap year. 388 +/ 389 @property bool isLeapYear() const @safe pure nothrow @nogc 390 { 391 return yearIsLeapYear(year); 392 } 393 394 private void setDayOfYear(bool useExceptions = false)(int days) 395 { 396 immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; 397 398 bool dayOutOfRange = days <= 0 || days > (isLeapYear ? daysInLeapYear : daysInYear); 399 400 static if (useExceptions) 401 { 402 if (dayOutOfRange) throw InvalidDay; 403 } 404 else 405 { 406 assert(!dayOutOfRange, "Invalid Day"); 407 } 408 409 foreach (i; 1 .. lastDay.length) 410 { 411 if (days <= lastDay[i]) 412 { 413 month = cast(Month)(cast(int) Month.jan + i - 1); 414 day = cast(ubyte)(days - lastDay[i - 1]); 415 return; 416 } 417 } 418 assert(0, "Invalid day of the year."); 419 } 420 421 /++ 422 The last day in the month that this $(LREF Date) is in. 423 +/ 424 @property ubyte daysInMonth() const @safe pure nothrow @nogc 425 { 426 return maxDay(year, month); 427 } 428 429 /++ 430 Whether the current year is a date in A.D. 431 +/ 432 @property bool isAD() const @safe pure nothrow @nogc 433 { 434 return year > 0; 435 } 436 } 437 438 /++ 439 Represents a date in the 440 $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic 441 Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years 442 are A.D. Non-positive years are B.C. 443 444 Year, month, and day are kept separately internally so that $(D Date) is 445 optimized for calendar-based operations. 446 447 $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian 448 leap year calculations for its entire length. As per 449 $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as 450 year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. 451 as a positive integer with 1 B.C. being the year prior to 1 A.D. 452 453 Year 0 is a leap year. 454 +/ 455 extern(C++, "boost", "gregorian") 456 extern(C++, class) 457 @serdeProxy!YearMonthDay 458 struct date 459 { 460 extern(D): 461 public: 462 463 private enum _julianShift = 1_721_425; 464 465 /// 466 uint toHash() @safe pure nothrow @nogc const scope 467 { 468 return _julianDay; 469 } 470 471 /++ 472 Throws: 473 $(LREF DateTimeException) if the resulting 474 $(LREF Date) would not be valid. 475 476 Params: 477 _year = Year of the Gregorian Calendar. Positive values are A.D. 478 Non-positive values are B.C. with year 0 being the year 479 prior to 1 A.D. 480 _month = Month of the year (January is 1). 481 _day = Day of the month. 482 +/ 483 pragma(inline, false) 484 static Date trustedCreate(int _year, int _month, int _day) @safe pure @nogc nothrow 485 { 486 Date ret; 487 immutable int[] lastDay = yearIsLeapYear(_year) ? lastDayLeap : lastDayNonLeap; 488 auto monthIndex = _month - Month.jan; 489 490 const dayOfYear = lastDay[monthIndex] + _day; 491 492 if (_month >= Month.jan && _month <= Month.dec) {} else 493 assert(0, "Invalid month."); 494 if (_year > 0) 495 { 496 if (_year == 1) 497 { 498 ret._julianDay = dayOfYear; 499 goto R; 500 } 501 502 int years = _year - 1; 503 auto days = (years / 400) * daysIn400Years; 504 years %= 400; 505 506 days += (years / 100) * daysIn100Years; 507 years %= 100; 508 509 days += (years / 4) * daysIn4Years; 510 years %= 4; 511 512 days += years * daysInYear; 513 514 days += dayOfYear; 515 516 ret._julianDay = days; 517 } 518 else if (_year == 0) 519 { 520 ret._julianDay = dayOfYear - daysInLeapYear; 521 } 522 else 523 { 524 int years = _year; 525 auto days = (years / 400) * daysIn400Years; 526 years %= 400; 527 528 days += (years / 100) * daysIn100Years; 529 years %= 100; 530 531 days += (years / 4) * daysIn4Years; 532 years %= 4; 533 534 if (years < 0) 535 { 536 days -= daysInLeapYear; 537 ++years; 538 539 days += years * daysInYear; 540 541 days -= daysInYear - dayOfYear; 542 } 543 else 544 days -= daysInLeapYear - dayOfYear; 545 546 ret._julianDay = days; 547 } 548 R: 549 ret._julianDay += _julianShift; 550 return ret; 551 } 552 553 /// 554 Timestamp timestamp() @safe pure nothrow @nogc const @property 555 { 556 return yearMonthDay.timestamp; 557 } 558 559 version(D_Exceptions) 560 /// 561 this(Timestamp timestamp) @safe pure @nogc 562 { 563 if (timestamp.precision != Timestamp.Precision.day) 564 { 565 static immutable exc = new Exception("Date: invalid timestamp precision"); 566 } 567 } 568 569 version(D_Exceptions) 570 /// 571 this(scope const(char)[] str) @safe pure @nogc 572 { 573 this = fromString(str); 574 } 575 576 version(D_Exceptions) 577 /// 578 this(YearMonthDay ymd) @safe pure @nogc 579 { 580 with(ymd) this(year, month, day); 581 } 582 583 version(D_Exceptions) 584 /// 585 this(int _year, int _month, int _day) @safe pure @nogc 586 { 587 if (!valid!"months"(_month)) 588 throw InvalidMonth; 589 if (!valid!"days"(_year, cast(Month) _month, _day)) 590 throw InvalidDay; 591 this = trustedCreate(_year, _month, _day); 592 } 593 594 /// 595 static bool fromYMD(int _year, int _month, int _day, out Date value) @safe pure nothrow @nogc 596 { 597 if (valid!"months"(_month) && valid!"days"(_year, cast(Month) _month, _day)) 598 { 599 value = trustedCreate(_year, _month, _day); 600 return true; 601 } 602 return false; 603 } 604 605 version (mir_test) 606 @safe unittest 607 { 608 import std.exception : assertNotThrown; 609 // assert(Date(0, 12, 31) == Date.init); 610 611 // Test A.D. 612 assertThrown!DateTimeException(Date(1, 0, 1)); 613 assertThrown!DateTimeException(Date(1, 1, 0)); 614 assertThrown!DateTimeException(Date(1999, 13, 1)); 615 assertThrown!DateTimeException(Date(1999, 1, 32)); 616 assertThrown!DateTimeException(Date(1999, 2, 29)); 617 assertThrown!DateTimeException(Date(2000, 2, 30)); 618 assertThrown!DateTimeException(Date(1999, 3, 32)); 619 assertThrown!DateTimeException(Date(1999, 4, 31)); 620 assertThrown!DateTimeException(Date(1999, 5, 32)); 621 assertThrown!DateTimeException(Date(1999, 6, 31)); 622 assertThrown!DateTimeException(Date(1999, 7, 32)); 623 assertThrown!DateTimeException(Date(1999, 8, 32)); 624 assertThrown!DateTimeException(Date(1999, 9, 31)); 625 assertThrown!DateTimeException(Date(1999, 10, 32)); 626 assertThrown!DateTimeException(Date(1999, 11, 31)); 627 assertThrown!DateTimeException(Date(1999, 12, 32)); 628 629 assertNotThrown!DateTimeException(Date(1999, 1, 31)); 630 assertNotThrown!DateTimeException(Date(1999, 2, 28)); 631 assertNotThrown!DateTimeException(Date(2000, 2, 29)); 632 assertNotThrown!DateTimeException(Date(1999, 3, 31)); 633 assertNotThrown!DateTimeException(Date(1999, 4, 30)); 634 assertNotThrown!DateTimeException(Date(1999, 5, 31)); 635 assertNotThrown!DateTimeException(Date(1999, 6, 30)); 636 assertNotThrown!DateTimeException(Date(1999, 7, 31)); 637 assertNotThrown!DateTimeException(Date(1999, 8, 31)); 638 assertNotThrown!DateTimeException(Date(1999, 9, 30)); 639 assertNotThrown!DateTimeException(Date(1999, 10, 31)); 640 assertNotThrown!DateTimeException(Date(1999, 11, 30)); 641 assertNotThrown!DateTimeException(Date(1999, 12, 31)); 642 643 // Test B.C. 644 assertNotThrown!DateTimeException(Date(0, 1, 1)); 645 assertNotThrown!DateTimeException(Date(-1, 1, 1)); 646 assertNotThrown!DateTimeException(Date(-1, 12, 31)); 647 assertNotThrown!DateTimeException(Date(-1, 2, 28)); 648 assertNotThrown!DateTimeException(Date(-4, 2, 29)); 649 650 assertThrown!DateTimeException(Date(-1, 2, 29)); 651 assertThrown!DateTimeException(Date(-2, 2, 29)); 652 assertThrown!DateTimeException(Date(-3, 2, 29)); 653 } 654 655 656 /++ 657 Params: 658 day = Julian day. 659 +/ 660 this(int day) @safe pure nothrow @nogc 661 { 662 _julianDay = day; 663 } 664 665 version (mir_test) 666 @safe unittest 667 { 668 import std.range : chain; 669 670 // Test A.D. 671 // foreach (gd; chain(testGregDaysBC, testGregDaysAD)) 672 // assert(Date(gd.day) == gd.date); 673 } 674 675 676 /++ 677 Compares this $(LREF Date) with the given $(LREF Date). 678 679 Returns: 680 $(BOOKTABLE, 681 $(TR $(TD this < rhs) $(TD < 0)) 682 $(TR $(TD this == rhs) $(TD 0)) 683 $(TR $(TD this > rhs) $(TD > 0)) 684 ) 685 +/ 686 int opCmp(Date rhs) const @safe pure nothrow @nogc 687 { 688 return this._julianDay - rhs._julianDay; 689 } 690 691 version (mir_test) 692 @safe unittest 693 { 694 // Test A.D. 695 // assert(Date(0, 12, 31).opCmp(Date.init) == 0); 696 697 assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); 698 assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); 699 assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); 700 701 assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); 702 assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); 703 704 assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); 705 706 assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); 707 assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); 708 assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); 709 assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); 710 assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); 711 assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); 712 713 assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); 714 assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); 715 assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); 716 assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); 717 assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); 718 assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); 719 720 // Test B.C. 721 assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); 722 assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); 723 assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); 724 assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); 725 726 assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); 727 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); 728 729 assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); 730 731 assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); 732 assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); 733 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); 734 assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); 735 assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); 736 assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); 737 738 assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); 739 assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); 740 assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); 741 assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); 742 assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); 743 assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); 744 745 // Test Both 746 assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); 747 assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); 748 749 assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); 750 assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); 751 752 assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); 753 assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); 754 755 assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); 756 assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); 757 758 assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); 759 assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); 760 761 auto date = Date(1999, 7, 6); 762 const cdate = Date(1999, 7, 6); 763 immutable idate = Date(1999, 7, 6); 764 assert(date.opCmp(date) == 0); 765 assert(date.opCmp(cdate) == 0); 766 assert(date.opCmp(idate) == 0); 767 assert(cdate.opCmp(date) == 0); 768 assert(cdate.opCmp(cdate) == 0); 769 assert(cdate.opCmp(idate) == 0); 770 assert(idate.opCmp(date) == 0); 771 assert(idate.opCmp(cdate) == 0); 772 assert(idate.opCmp(idate) == 0); 773 } 774 775 /++ 776 Day of the week this $(LREF Date) is on. 777 +/ 778 @property DayOfWeek dayOfWeek() const @safe pure nothrow @nogc 779 { 780 return getDayOfWeek(_julianDay); 781 } 782 783 version (mir_test) 784 @safe unittest 785 { 786 const cdate = Date(1999, 7, 6); 787 immutable idate = Date(1999, 7, 6); 788 assert(cdate.dayOfWeek == DayOfWeek.tue); 789 static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); 790 assert(idate.dayOfWeek == DayOfWeek.tue); 791 static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); 792 } 793 794 /++ 795 The Xth day of the Gregorian Calendar that this $(LREF Date) is on. 796 +/ 797 @property int dayOfGregorianCal() const @safe pure nothrow @nogc 798 { 799 return _julianDay - _julianShift; 800 } 801 802 /// 803 version (mir_test) 804 @safe unittest 805 { 806 assert(Date(1, 1, 1).dayOfGregorianCal == 1); 807 assert(Date(1, 12, 31).dayOfGregorianCal == 365); 808 assert(Date(2, 1, 1).dayOfGregorianCal == 366); 809 810 assert(Date(0, 12, 31).dayOfGregorianCal == 0); 811 assert(Date(0, 1, 1).dayOfGregorianCal == -365); 812 assert(Date(-1, 12, 31).dayOfGregorianCal == -366); 813 814 assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); 815 assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); 816 } 817 818 version (mir_test) 819 @safe unittest 820 { 821 import std.range : chain; 822 823 foreach (gd; chain(testGregDaysBC, testGregDaysAD)) 824 assert(gd.date.dayOfGregorianCal == gd.day); 825 826 auto date = Date(1999, 7, 6); 827 const cdate = Date(1999, 7, 6); 828 immutable idate = Date(1999, 7, 6); 829 assert(date.dayOfGregorianCal == 729_941); 830 assert(cdate.dayOfGregorianCal == 729_941); 831 assert(idate.dayOfGregorianCal == 729_941); 832 } 833 834 /++ 835 The Xth day of the Gregorian Calendar that this $(LREF Date) is on. 836 837 Params: 838 day = The day of the Gregorian Calendar to set this $(LREF Date) to. 839 +/ 840 @property void dayOfGregorianCal(int day) @safe pure nothrow @nogc 841 { 842 this = Date(day + _julianShift); 843 } 844 845 /// 846 version (mir_test) 847 @safe unittest 848 { 849 auto date = Date.init; 850 date.dayOfGregorianCal = 1; 851 assert(date == Date(1, 1, 1)); 852 853 date.dayOfGregorianCal = 365; 854 assert(date == Date(1, 12, 31)); 855 856 date.dayOfGregorianCal = 366; 857 assert(date == Date(2, 1, 1)); 858 859 date.dayOfGregorianCal = 0; 860 assert(date == Date(0, 12, 31)); 861 862 date.dayOfGregorianCal = -365; 863 assert(date == Date(-0, 1, 1)); 864 865 date.dayOfGregorianCal = -366; 866 assert(date == Date(-1, 12, 31)); 867 868 date.dayOfGregorianCal = 730_120; 869 assert(date == Date(2000, 1, 1)); 870 871 date.dayOfGregorianCal = 734_137; 872 assert(date == Date(2010, 12, 31)); 873 } 874 875 version (mir_test) 876 @safe unittest 877 { 878 auto date = Date(1999, 7, 6); 879 const cdate = Date(1999, 7, 6); 880 immutable idate = Date(1999, 7, 6); 881 date.dayOfGregorianCal = 187; 882 assert(date.dayOfGregorianCal == 187); 883 static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); 884 static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); 885 } 886 887 private enum uint _startDict = Date(1900, 1, 1)._julianDay; // [ 888 private enum uint _endDict = Date(2040, 1, 1)._julianDay; // ) 889 static immutable _dict = () 890 { 891 YearMonthDay[Date._endDict - Date._startDict] dict; 892 foreach (uint i; 0 .. dict.length) 893 dict[i] = Date(i + Date._startDict).yearMonthDayImpl; 894 return dict; 895 }(); 896 897 /// 898 YearMonthDay yearMonthDay() const @safe pure nothrow @nogc @property 899 { 900 uint day = _julianDay; 901 if (day < _endDict) 902 { 903 import mir.checkedint: subu; 904 bool overflow; 905 auto index = subu(day, _startDict, overflow); 906 if (!overflow) 907 return _dict[index]; 908 } 909 return yearMonthDayImpl; 910 } 911 912 /// 913 short year() const @safe pure nothrow @nogc @property 914 { 915 return yearMonthDay.year; 916 } 917 918 /// 919 Month month() const @safe pure nothrow @nogc @property 920 { 921 return yearMonthDay.month; 922 } 923 924 /// 925 ubyte day() const @safe pure nothrow @nogc @property 926 { 927 return yearMonthDay.day; 928 } 929 930 pragma(inline, false) 931 YearMonthDay yearMonthDayImpl() const @safe pure nothrow @nogc @property 932 { 933 YearMonthDay ymd; 934 int days = dayOfGregorianCal; 935 with(ymd) 936 if (days > 0) 937 { 938 int years = (days / daysIn400Years) * 400 + 1; 939 days %= daysIn400Years; 940 941 { 942 immutable tempYears = days / daysIn100Years; 943 944 if (tempYears == 4) 945 { 946 years += 300; 947 days -= daysIn100Years * 3; 948 } 949 else 950 { 951 years += tempYears * 100; 952 days %= daysIn100Years; 953 } 954 } 955 956 years += (days / daysIn4Years) * 4; 957 days %= daysIn4Years; 958 959 { 960 immutable tempYears = days / daysInYear; 961 962 if (tempYears == 4) 963 { 964 years += 3; 965 days -= daysInYear * 3; 966 } 967 else 968 { 969 years += tempYears; 970 days %= daysInYear; 971 } 972 } 973 974 if (days == 0) 975 { 976 year = cast(short)(years - 1); 977 month = Month.dec; 978 day = 31; 979 } 980 else 981 { 982 year = cast(short) years; 983 984 setDayOfYear(days); 985 } 986 } 987 else if (days <= 0 && -days < daysInLeapYear) 988 { 989 year = 0; 990 991 setDayOfYear(daysInLeapYear + days); 992 } 993 else 994 { 995 days += daysInLeapYear - 1; 996 int years = (days / daysIn400Years) * 400 - 1; 997 days %= daysIn400Years; 998 999 { 1000 immutable tempYears = days / daysIn100Years; 1001 1002 if (tempYears == -4) 1003 { 1004 years -= 300; 1005 days += daysIn100Years * 3; 1006 } 1007 else 1008 { 1009 years += tempYears * 100; 1010 days %= daysIn100Years; 1011 } 1012 } 1013 1014 years += (days / daysIn4Years) * 4; 1015 days %= daysIn4Years; 1016 1017 { 1018 immutable tempYears = days / daysInYear; 1019 1020 if (tempYears == -4) 1021 { 1022 years -= 3; 1023 days += daysInYear * 3; 1024 } 1025 else 1026 { 1027 years += tempYears; 1028 days %= daysInYear; 1029 } 1030 } 1031 1032 if (days == 0) 1033 { 1034 year = cast(short)(years + 1); 1035 month = Month.jan; 1036 day = 1; 1037 } 1038 else 1039 { 1040 year = cast(short) years; 1041 immutable newDoY = (yearIsLeapYear(year) ? daysInLeapYear : daysInYear) + days + 1; 1042 1043 setDayOfYear(newDoY); 1044 } 1045 } 1046 return ymd; 1047 } 1048 1049 /++ 1050 $(LREF Date) for the last day in the quarter that this $(LREF Date) is in. 1051 +/ 1052 @property Date endOfQuarter() const @safe pure nothrow @nogc 1053 { 1054 with(yearMonthDay) 1055 { 1056 int d = _julianDay - day; 1057 final switch (month) with(Month) 1058 { 1059 case jan: d += maxDay(year, jan); goto case; 1060 case feb: d += maxDay(year, feb); goto case; 1061 case mar: d += maxDay(year, mar); break; 1062 1063 case apr: d += maxDay(year, apr); goto case; 1064 case may: d += maxDay(year, may); goto case; 1065 case jun: d += maxDay(year, jun); break; 1066 1067 case jul: d += maxDay(year, jul); goto case; 1068 case aug: d += maxDay(year, aug); goto case; 1069 case sep: d += maxDay(year, sep); break; 1070 1071 case oct: d += maxDay(year, oct); goto case; 1072 case nov: d += maxDay(year, nov); goto case; 1073 case dec: d += maxDay(year, dec); break; 1074 } 1075 return Date(d); 1076 } 1077 } 1078 1079 /// 1080 version (mir_test) 1081 @safe unittest 1082 { 1083 assert(Date(1999, 1, 6).endOfQuarter == Date(1999, 3, 31)); 1084 assert(Date(1999, 2, 7).endOfQuarter == Date(1999, 3, 31)); 1085 assert(Date(2000, 2, 7).endOfQuarter == Date(2000, 3, 31)); 1086 assert(Date(2000, 6, 4).endOfQuarter == Date(2000, 6, 30)); 1087 } 1088 1089 /++ 1090 $(LREF Date) for the last day in the month that this $(LREF Date) is in. 1091 +/ 1092 @property Date endOfMonth() const @safe pure nothrow @nogc 1093 { 1094 with(yearMonthDay) 1095 return Date(_julianDay + maxDay(year, month) - day); 1096 } 1097 1098 /// 1099 version (mir_test) 1100 @safe unittest 1101 { 1102 assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); 1103 assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); 1104 assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); 1105 assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); 1106 } 1107 1108 version (mir_test) 1109 @safe unittest 1110 { 1111 // Test A.D. 1112 assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); 1113 assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); 1114 assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); 1115 assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); 1116 assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); 1117 assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); 1118 assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); 1119 assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); 1120 assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); 1121 assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); 1122 assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); 1123 assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); 1124 assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); 1125 1126 // Test B.C. 1127 assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); 1128 assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); 1129 assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); 1130 assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); 1131 assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); 1132 assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); 1133 assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); 1134 assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); 1135 assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); 1136 assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); 1137 assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); 1138 assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); 1139 assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); 1140 1141 const cdate = Date(1999, 7, 6); 1142 immutable idate = Date(1999, 7, 6); 1143 static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); 1144 static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); 1145 } 1146 1147 /// 1148 int opBinary(string op : "-")(Date lhs) const 1149 { 1150 return _julianDay - lhs._julianDay; 1151 } 1152 1153 /// 1154 Date opBinary(string op : "+")(int lhs) const 1155 { 1156 return Date(_julianDay + lhs); 1157 } 1158 1159 /// 1160 Date opBinaryRight(string op : "+")(int lhs) const 1161 { 1162 return Date(_julianDay + lhs); 1163 } 1164 1165 /// 1166 Date opBinary(string op : "-")(int lhs) const 1167 { 1168 return Date(_julianDay - lhs); 1169 } 1170 1171 const nothrow @nogc pure @safe 1172 Date add(string units)(long amount, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) 1173 { 1174 with(yearMonthDay.add!units(amount)) return trustedCreate(year, month, day); 1175 } 1176 1177 /++ 1178 The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this 1179 $(LREF Date) at noon (since the Julian day changes at noon). 1180 +/ 1181 @property int julianDay() const @safe pure nothrow @nogc 1182 { 1183 return _julianDay; 1184 } 1185 1186 version (mir_test) 1187 @safe unittest 1188 { 1189 assert(Date(-4713, 11, 24).julianDay == 0); 1190 assert(Date(0, 12, 31).julianDay == _julianShift); 1191 assert(Date(1, 1, 1).julianDay == 1_721_426); 1192 assert(Date(1582, 10, 15).julianDay == 2_299_161); 1193 assert(Date(1858, 11, 17).julianDay == 2_400_001); 1194 assert(Date(1982, 1, 4).julianDay == 2_444_974); 1195 assert(Date(1996, 3, 31).julianDay == 2_450_174); 1196 assert(Date(2010, 8, 24).julianDay == 2_455_433); 1197 1198 const cdate = Date(1999, 7, 6); 1199 immutable idate = Date(1999, 7, 6); 1200 assert(cdate.julianDay == 2_451_366); 1201 assert(idate.julianDay == 2_451_366); 1202 } 1203 1204 1205 /++ 1206 The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for 1207 any time on this date (since, the modified Julian day changes at 1208 midnight). 1209 +/ 1210 @property long modJulianDay() const @safe pure nothrow @nogc 1211 { 1212 return julianDay - 2_400_001; 1213 } 1214 1215 version (mir_test) 1216 @safe unittest 1217 { 1218 assert(Date(1858, 11, 17).modJulianDay == 0); 1219 assert(Date(2010, 8, 24).modJulianDay == 55_432); 1220 1221 const cdate = Date(1999, 7, 6); 1222 immutable idate = Date(1999, 7, 6); 1223 assert(cdate.modJulianDay == 51_365); 1224 assert(idate.modJulianDay == 51_365); 1225 } 1226 1227 version(D_BetterC){} else 1228 private string toStringImpl(alias fun)() const @safe pure nothrow 1229 { 1230 import mir.appender: UnsafeArrayBuffer; 1231 char[16] buffer = void; 1232 auto w = UnsafeArrayBuffer!char(buffer); 1233 fun(w); 1234 return w.data.idup; 1235 } 1236 1237 version(D_BetterC){} else 1238 /++ 1239 Converts this $(LREF Date) to a string with the format `YYYYMMDD`. 1240 If `writer` is set, the resulting string will be written directly 1241 to it. 1242 1243 Returns: 1244 A `string` when not using an output range; `void` otherwise. 1245 +/ 1246 string toISOString() const @safe pure nothrow 1247 { 1248 return toStringImpl!toISOString; 1249 } 1250 1251 /// 1252 version (mir_test) 1253 @safe unittest 1254 { 1255 assert(Date.init.toISOString == "null"); 1256 assert(Date(2010, 7, 4).toISOString == "20100704"); 1257 assert(Date(1998, 12, 25).toISOString == "19981225"); 1258 assert(Date(0, 1, 5).toISOString == "00000105"); 1259 assert(Date(-4, 1, 5).toISOString == "-00040105", Date(-4, 1, 5).toISOString()); 1260 } 1261 1262 version (mir_test) 1263 @safe unittest 1264 { 1265 // Test A.D. 1266 assert(Date(9, 12, 4).toISOString == "00091204"); 1267 assert(Date(99, 12, 4).toISOString == "00991204"); 1268 assert(Date(999, 12, 4).toISOString == "09991204"); 1269 assert(Date(9999, 7, 4).toISOString == "99990704"); 1270 assert(Date(10000, 10, 20).toISOString == "+100001020"); 1271 1272 // Test B.C. 1273 assert(Date(0, 12, 4).toISOString == "00001204"); 1274 assert(Date(-9, 12, 4).toISOString == "-00091204"); 1275 assert(Date(-99, 12, 4).toISOString == "-00991204"); 1276 assert(Date(-999, 12, 4).toISOString == "-09991204"); 1277 assert(Date(-9999, 7, 4).toISOString == "-99990704"); 1278 assert(Date(-10000, 10, 20).toISOString == "-100001020"); 1279 1280 const cdate = Date(1999, 7, 6); 1281 immutable idate = Date(1999, 7, 6); 1282 assert(cdate.toISOString == "19990706"); 1283 assert(idate.toISOString == "19990706"); 1284 } 1285 1286 /// ditto 1287 void toISOString(W)(scope ref W w) const scope 1288 if (isOutputRange!(W, char)) 1289 { 1290 import mir.format: printZeroPad; 1291 if(this == Date.init) 1292 { 1293 w.put("null"); 1294 return; 1295 } 1296 with(yearMonthDay) 1297 { 1298 if (year >= 10_000) 1299 w.put('+'); 1300 w.printZeroPad(year, year >= 0 ? year < 10_000 ? 4 : 5 : year > -10_000 ? 5 : 6); 1301 w.printZeroPad(cast(uint)month, 2); 1302 w.printZeroPad(day, 2); 1303 } 1304 } 1305 1306 version (mir_test) 1307 @safe unittest 1308 { 1309 auto date = Date(1999, 7, 6); 1310 const cdate = Date(1999, 7, 6); 1311 immutable idate = Date(1999, 7, 6); 1312 assert(date.toString); 1313 assert(cdate.toString); 1314 assert(idate.toString); 1315 } 1316 1317 version(D_BetterC){} else 1318 /++ 1319 Converts this $(LREF Date) to a string with the format `YYYY-MM-DD`. 1320 If `writer` is set, the resulting string will be written directly 1321 to it. 1322 1323 Returns: 1324 A `string` when not using an output range; `void` otherwise. 1325 +/ 1326 string toISOExtString() const @safe pure nothrow 1327 { 1328 return toStringImpl!toISOExtString; 1329 } 1330 1331 ///ditto 1332 alias toString = toISOExtString; 1333 1334 /// 1335 version (mir_test) 1336 @safe unittest 1337 { 1338 assert(Date.init.toISOExtString == "null"); 1339 assert(Date(2010, 7, 4).toISOExtString == "2010-07-04"); 1340 assert(Date(1998, 12, 25).toISOExtString == "1998-12-25"); 1341 assert(Date(0, 1, 5).toISOExtString == "0000-01-05"); 1342 assert(Date(-4, 1, 5).toISOExtString == "-0004-01-05"); 1343 } 1344 1345 version (mir_test) 1346 @safe pure unittest 1347 { 1348 import std.array : appender; 1349 1350 auto w = appender!(char[])(); 1351 Date(2010, 7, 4).toISOString(w); 1352 assert(w.data == "20100704"); 1353 w.clear(); 1354 Date(1998, 12, 25).toISOString(w); 1355 assert(w.data == "19981225"); 1356 } 1357 1358 version (mir_test) 1359 @safe unittest 1360 { 1361 // Test A.D. 1362 assert(Date(9, 12, 4).toISOExtString == "0009-12-04"); 1363 assert(Date(99, 12, 4).toISOExtString == "0099-12-04"); 1364 assert(Date(999, 12, 4).toISOExtString == "0999-12-04"); 1365 assert(Date(9999, 7, 4).toISOExtString == "9999-07-04"); 1366 assert(Date(10000, 10, 20).toISOExtString == "+10000-10-20"); 1367 1368 // Test B.C. 1369 assert(Date(0, 12, 4).toISOExtString == "0000-12-04"); 1370 assert(Date(-9, 12, 4).toISOExtString == "-0009-12-04"); 1371 assert(Date(-99, 12, 4).toISOExtString == "-0099-12-04"); 1372 assert(Date(-999, 12, 4).toISOExtString == "-0999-12-04"); 1373 assert(Date(-9999, 7, 4).toISOExtString == "-9999-07-04"); 1374 assert(Date(-10000, 10, 20).toISOExtString == "-10000-10-20"); 1375 1376 const cdate = Date(1999, 7, 6); 1377 immutable idate = Date(1999, 7, 6); 1378 assert(cdate.toISOExtString == "1999-07-06"); 1379 assert(idate.toISOExtString == "1999-07-06"); 1380 } 1381 1382 /// ditto 1383 void toISOExtString(W)(scope ref W w) const scope 1384 if (isOutputRange!(W, char)) 1385 { 1386 import mir.format: printZeroPad; 1387 if(this == Date.init) 1388 { 1389 w.put("null"); 1390 return; 1391 } 1392 with(yearMonthDay) 1393 { 1394 if (year >= 10_000) 1395 w.put('+'); 1396 w.printZeroPad(year, year >= 0 ? year < 10_000 ? 4 : 5 : year > -10_000 ? 5 : 6); 1397 w.put('-'); 1398 w.printZeroPad(cast(uint)month, 2); 1399 w.put('-'); 1400 w.printZeroPad(day, 2); 1401 } 1402 } 1403 1404 version (mir_test) 1405 @safe pure unittest 1406 { 1407 import std.array : appender; 1408 1409 auto w = appender!(char[])(); 1410 Date(2010, 7, 4).toISOExtString(w); 1411 assert(w.data == "2010-07-04"); 1412 w.clear(); 1413 Date(-4, 1, 5).toISOExtString(w); 1414 assert(w.data == "-0004-01-05"); 1415 } 1416 1417 version(D_BetterC){} else 1418 /++ 1419 Converts this $(LREF Date) to a string with the format `YYYY-Mon-DD`. 1420 If `writer` is set, the resulting string will be written directly 1421 to it. 1422 1423 Returns: 1424 A `string` when not using an output range; `void` otherwise. 1425 +/ 1426 string toSimpleString() const @safe pure nothrow 1427 { 1428 return toStringImpl!toSimpleString; 1429 } 1430 1431 /// 1432 version (mir_test) 1433 @safe unittest 1434 { 1435 assert(Date.init.toSimpleString == "null"); 1436 assert(Date(2010, 7, 4).toSimpleString == "2010-Jul-04"); 1437 assert(Date(1998, 12, 25).toSimpleString == "1998-Dec-25"); 1438 assert(Date(0, 1, 5).toSimpleString == "0000-Jan-05"); 1439 assert(Date(-4, 1, 5).toSimpleString == "-0004-Jan-05"); 1440 } 1441 1442 version (mir_test) 1443 @safe unittest 1444 { 1445 // Test A.D. 1446 assert(Date(9, 12, 4).toSimpleString == "0009-Dec-04"); 1447 assert(Date(99, 12, 4).toSimpleString == "0099-Dec-04"); 1448 assert(Date(999, 12, 4).toSimpleString == "0999-Dec-04"); 1449 assert(Date(9999, 7, 4).toSimpleString == "9999-Jul-04"); 1450 assert(Date(10000, 10, 20).toSimpleString == "+10000-Oct-20"); 1451 1452 // Test B.C. 1453 assert(Date(0, 12, 4).toSimpleString == "0000-Dec-04"); 1454 assert(Date(-9, 12, 4).toSimpleString == "-0009-Dec-04"); 1455 assert(Date(-99, 12, 4).toSimpleString == "-0099-Dec-04"); 1456 assert(Date(-999, 12, 4).toSimpleString == "-0999-Dec-04"); 1457 assert(Date(-9999, 7, 4).toSimpleString == "-9999-Jul-04"); 1458 assert(Date(-10000, 10, 20).toSimpleString == "-10000-Oct-20"); 1459 1460 const cdate = Date(1999, 7, 6); 1461 immutable idate = Date(1999, 7, 6); 1462 assert(cdate.toSimpleString == "1999-Jul-06"); 1463 assert(idate.toSimpleString == "1999-Jul-06"); 1464 } 1465 1466 /// ditto 1467 void toSimpleString(W)(scope ref W w) const scope 1468 if (isOutputRange!(W, char)) 1469 { 1470 import mir.format: printZeroPad; 1471 if(this == Date.init) 1472 { 1473 w.put("null"); 1474 return; 1475 } 1476 with(yearMonthDay) 1477 { 1478 if (year >= 10_000) 1479 w.put('+'); 1480 w.printZeroPad(year, year >= 0 ? year < 10_000 ? 4 : 5 : year > -10_000 ? 5 : 6); 1481 w.put('-'); 1482 w.put(month.monthToString); 1483 w.put('-'); 1484 w.printZeroPad(day, 2); 1485 } 1486 } 1487 1488 version (mir_test) 1489 @safe pure unittest 1490 { 1491 import std.array : appender; 1492 1493 auto w = appender!(char[])(); 1494 Date(9, 12, 4).toSimpleString(w); 1495 assert(w.data == "0009-Dec-04"); 1496 w.clear(); 1497 Date(-10000, 10, 20).toSimpleString(w); 1498 assert(w.data == "-10000-Oct-20"); 1499 } 1500 1501 /++ 1502 Creates a $(LREF Date) from a string with the format YYYYMMDD. 1503 1504 Params: 1505 str = A string formatted in the way that $(LREF .date.toISOString) formats dates. 1506 value = (optional) result value. 1507 1508 Throws: 1509 $(LREF DateTimeException) if the given string is 1510 not in the correct format or if the resulting $(LREF Date) would not 1511 be valid. Two arguments overload is `nothrow`. 1512 Returns: 1513 `bool` on success for two arguments overload, and the resulting date for single argument overdload. 1514 +/ 1515 static bool fromISOString(C)(scope const(C)[] str, out Date value) @safe pure nothrow @nogc 1516 if (isSomeChar!C) 1517 { 1518 import mir.parse: fromString; 1519 1520 if (str.length < 8) 1521 return false; 1522 1523 auto yearStr = str[0 .. $ - 4]; 1524 1525 if ((yearStr[0] == '+' || yearStr[0] == '-') != (yearStr.length > 4)) 1526 return false; 1527 1528 uint day, month; 1529 int year; 1530 1531 return 1532 fromString(str[$ - 2 .. $], day) 1533 && fromString(str[$ - 4 .. $ - 2], month) 1534 && fromString(yearStr, year) 1535 && fromYMD(year, month, day, value); 1536 } 1537 1538 /// ditto 1539 static Date fromISOString(C)(scope const(C)[] str) @safe pure 1540 if (isSomeChar!C) 1541 { 1542 Date ret; 1543 if (fromISOString(str, ret)) 1544 return ret; 1545 throw InvalidISOString; 1546 } 1547 1548 /// 1549 version (mir_test) 1550 @safe unittest 1551 { 1552 assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); 1553 assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); 1554 assert(Date.fromISOString("00000105") == Date(0, 1, 5)); 1555 assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); 1556 } 1557 1558 version (mir_test) 1559 @safe unittest 1560 { 1561 assertThrown!DateTimeException(Date.fromISOString("")); 1562 assertThrown!DateTimeException(Date.fromISOString("990704")); 1563 assertThrown!DateTimeException(Date.fromISOString("0100704")); 1564 assertThrown!DateTimeException(Date.fromISOString("2010070")); 1565 assertThrown!DateTimeException(Date.fromISOString("120100704")); 1566 assertThrown!DateTimeException(Date.fromISOString("-0100704")); 1567 assertThrown!DateTimeException(Date.fromISOString("+0100704")); 1568 assertThrown!DateTimeException(Date.fromISOString("2010070a")); 1569 assertThrown!DateTimeException(Date.fromISOString("20100a04")); 1570 assertThrown!DateTimeException(Date.fromISOString("2010a704")); 1571 1572 assertThrown!DateTimeException(Date.fromISOString("99-07-04")); 1573 assertThrown!DateTimeException(Date.fromISOString("010-07-04")); 1574 assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); 1575 assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); 1576 assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); 1577 assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); 1578 assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); 1579 assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); 1580 assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); 1581 assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); 1582 assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); 1583 assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); 1584 assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); 1585 assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); 1586 assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); 1587 assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); 1588 1589 assertThrown!DateTimeException(Date.fromISOString("99Jul04")); 1590 assertThrown!DateTimeException(Date.fromISOString("010Jul04")); 1591 assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); 1592 assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); 1593 assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); 1594 assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); 1595 assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); 1596 assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); 1597 assertThrown!DateTimeException(Date.fromISOString("2010aul04")); 1598 1599 assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); 1600 assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); 1601 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); 1602 assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); 1603 assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); 1604 assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); 1605 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); 1606 assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); 1607 assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); 1608 assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); 1609 1610 assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); 1611 assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); 1612 1613 assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); 1614 assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); 1615 assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); 1616 assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); 1617 } 1618 1619 // bug# 17801 1620 version (mir_test) 1621 @safe unittest 1622 { 1623 import std.conv : to; 1624 import std.meta : AliasSeq; 1625 static foreach (C; AliasSeq!(char, wchar, dchar)) 1626 { 1627 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) 1628 assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21)); 1629 } 1630 } 1631 1632 /++ 1633 Creates a $(LREF Date) from a string with the format YYYY-MM-DD. 1634 1635 Params: 1636 str = A string formatted in the way that $(LREF .date.toISOExtString) formats dates. 1637 value = (optional) result value. 1638 1639 Throws: 1640 $(LREF DateTimeException) if the given string is 1641 not in the correct format or if the resulting $(LREF Date) would not 1642 be valid. Two arguments overload is `nothrow`. 1643 Returns: 1644 `bool` on success for two arguments overload, and the resulting date for single argument overdload. 1645 +/ 1646 static bool fromISOExtString(C)(scope const(C)[] str, out Date value) @safe pure nothrow @nogc 1647 if (isSomeChar!C) 1648 { 1649 import mir.parse: fromString; 1650 1651 if (str.length < 10 || str[$-3] != '-' || str[$-6] != '-') 1652 return false; 1653 1654 auto yearStr = str[0 .. $ - 6]; 1655 1656 if ((yearStr[0] == '+' || yearStr[0] == '-') != (yearStr.length > 4)) 1657 return false; 1658 1659 uint day, month; 1660 int year; 1661 1662 return 1663 fromString(str[$ - 2 .. $], day) 1664 && fromString(str[$ - 5 .. $ - 3], month) 1665 && fromString(yearStr, year) 1666 && fromYMD(year, month, day, value); 1667 } 1668 1669 /// ditto 1670 static Date fromISOExtString(C)(scope const(C)[] str) @safe pure 1671 if (isSomeChar!C) 1672 { 1673 Date ret; 1674 if (fromISOExtString(str, ret)) 1675 return ret; 1676 throw InvalidISOExtendedString; 1677 } 1678 1679 /// 1680 version (mir_test) 1681 @safe unittest 1682 { 1683 assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); 1684 assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); 1685 assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); 1686 assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); 1687 } 1688 1689 version (mir_test) 1690 @safe unittest 1691 { 1692 assertThrown!DateTimeException(Date.fromISOExtString("")); 1693 assertThrown!DateTimeException(Date.fromISOExtString("990704")); 1694 assertThrown!DateTimeException(Date.fromISOExtString("0100704")); 1695 assertThrown!DateTimeException(Date.fromISOExtString("120100704")); 1696 assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); 1697 assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); 1698 assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); 1699 assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); 1700 assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); 1701 1702 assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); 1703 assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); 1704 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); 1705 assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); 1706 assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); 1707 assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); 1708 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); 1709 assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); 1710 assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); 1711 assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); 1712 assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); 1713 assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); 1714 assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); 1715 assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); 1716 assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); 1717 assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); 1718 1719 assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); 1720 assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); 1721 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); 1722 assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); 1723 assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); 1724 assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); 1725 assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); 1726 assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); 1727 assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); 1728 1729 assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); 1730 assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); 1731 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); 1732 assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); 1733 assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); 1734 assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); 1735 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); 1736 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); 1737 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); 1738 assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); 1739 1740 assertThrown!DateTimeException(Date.fromISOExtString("20100704")); 1741 assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); 1742 1743 assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); 1744 assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); 1745 assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); 1746 } 1747 1748 // bug# 17801 1749 version (mir_test) 1750 @safe unittest 1751 { 1752 import std.conv : to; 1753 import std.meta : AliasSeq; 1754 static foreach (C; AliasSeq!(char, wchar, dchar)) 1755 { 1756 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) 1757 assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21)); 1758 } 1759 } 1760 1761 1762 /++ 1763 Creates a $(LREF Date) from a string with the format YYYY-Mon-DD. 1764 1765 Params: 1766 str = A string formatted in the way that $(LREF .date.toSimpleString) formats dates. The function is case sensetive. 1767 value = (optional) result value. 1768 1769 Throws: 1770 $(LREF DateTimeException) if the given string is 1771 not in the correct format or if the resulting $(LREF Date) would not 1772 be valid. Two arguments overload is `nothrow`. 1773 Returns: 1774 `bool` on success for two arguments overload, and the resulting date for single argument overdload. 1775 +/ 1776 static bool fromSimpleString(C)(scope const(C)[] str, out Date value) @safe pure nothrow @nogc 1777 if (isSomeChar!C) 1778 { 1779 import mir.parse: fromString; 1780 1781 if (str.length < 11 || str[$-3] != '-' || str[$-7] != '-') 1782 return false; 1783 1784 auto yearStr = str[0 .. $ - 7]; 1785 1786 if ((yearStr[0] == '+' || yearStr[0] == '-') != (yearStr.length > 4)) 1787 return false; 1788 1789 Month month; 1790 1791 switch (str[$ - 6 .. $ - 3]) 1792 { 1793 case "Jan": month = Month.jan; break; 1794 case "Feb": month = Month.feb; break; 1795 case "Mar": month = Month.mar; break; 1796 case "Apr": month = Month.apr; break; 1797 case "May": month = Month.may; break; 1798 case "Jun": month = Month.jun; break; 1799 case "Jul": month = Month.jul; break; 1800 case "Aug": month = Month.aug; break; 1801 case "Sep": month = Month.sep; break; 1802 case "Oct": month = Month.oct; break; 1803 case "Nov": month = Month.nov; break; 1804 case "Dec": month = Month.dec; break; 1805 default: return false; 1806 } 1807 1808 uint day; 1809 int year; 1810 1811 return 1812 fromString(str[$ - 2 .. $], day) 1813 && fromString(yearStr, year) 1814 && fromYMD(year, month, day, value); 1815 } 1816 1817 /// ditto 1818 static Date fromSimpleString(C)(scope const(C)[] str) @safe pure 1819 if (isSomeChar!C) 1820 { 1821 Date ret; 1822 if (fromSimpleString(str, ret)) 1823 return ret; 1824 throw new DateTimeException("Invalid Simple String"); 1825 } 1826 1827 /// 1828 version (mir_test) 1829 @safe unittest 1830 { 1831 assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); 1832 assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); 1833 assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); 1834 assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); 1835 } 1836 1837 version (mir_test) 1838 @safe unittest 1839 { 1840 assertThrown!DateTimeException(Date.fromSimpleString("")); 1841 assertThrown!DateTimeException(Date.fromSimpleString("990704")); 1842 assertThrown!DateTimeException(Date.fromSimpleString("0100704")); 1843 assertThrown!DateTimeException(Date.fromSimpleString("2010070")); 1844 assertThrown!DateTimeException(Date.fromSimpleString("120100704")); 1845 assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); 1846 assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); 1847 assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); 1848 assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); 1849 assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); 1850 1851 assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); 1852 assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); 1853 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); 1854 assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); 1855 assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); 1856 assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); 1857 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); 1858 assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); 1859 assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); 1860 assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); 1861 assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); 1862 assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); 1863 assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); 1864 assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); 1865 assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); 1866 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); 1867 1868 assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); 1869 assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); 1870 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); 1871 assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); 1872 assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); 1873 assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); 1874 assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); 1875 assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); 1876 assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); 1877 1878 assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); 1879 assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); 1880 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); 1881 assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); 1882 assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); 1883 assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); 1884 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); 1885 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); 1886 assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); 1887 assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); 1888 1889 assertThrown!DateTimeException(Date.fromSimpleString("20100704")); 1890 assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); 1891 1892 assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); 1893 assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); 1894 assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); 1895 } 1896 1897 // bug# 17801 1898 version (mir_test) 1899 @safe unittest 1900 { 1901 import std.conv : to; 1902 import std.meta : AliasSeq; 1903 static foreach (C; AliasSeq!(char, wchar, dchar)) 1904 { 1905 static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) 1906 assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21)); 1907 } 1908 } 1909 1910 /++ 1911 Creates a $(LREF Date) from a string with the format YYYY-MM-DD, YYYYMMDD, or YYYY-Mon-DD. 1912 1913 Params: 1914 str = A string formatted in the way that $(LREF .date.toISOExtString), $(LREF .date.toISOString), and $(LREF .date.toSimpleString) format dates. The function is case sensetive. 1915 value = (optional) result value. 1916 1917 Throws: 1918 $(LREF DateTimeException) if the given string is 1919 not in the correct format or if the resulting $(LREF Date) would not 1920 be valid. Two arguments overload is `nothrow`. 1921 Returns: 1922 `bool` on success for two arguments overload, and the resulting date for single argument overdload. 1923 +/ 1924 static bool fromString(C)(scope const(C)[] str, out Date value) @safe pure nothrow @nogc 1925 { 1926 return fromISOExtString(str, value) 1927 || fromISOString(str, value) 1928 || fromSimpleString(str, value); 1929 } 1930 1931 /// 1932 version (mir_test) 1933 @safe pure @nogc unittest 1934 { 1935 assert(Date.fromString("2010-07-04") == Date(2010, 7, 4)); 1936 assert(Date.fromString("20100704") == Date(2010, 7, 4)); 1937 assert(Date.fromString("2010-Jul-04") == Date(2010, 7, 4)); 1938 } 1939 1940 /// ditto 1941 static Date fromString(C)(scope const(C)[] str) @safe pure 1942 if (isSomeChar!C) 1943 { 1944 Date ret; 1945 if (fromString(str, ret)) 1946 return ret; 1947 throw InvalidString; 1948 } 1949 1950 /++ 1951 Returns the $(LREF Date) farthest in the past which is representable by 1952 $(LREF Date). 1953 +/ 1954 @property static Date min() @safe pure nothrow @nogc 1955 { 1956 return Date(-(int.max / 2)); 1957 } 1958 1959 /++ 1960 Returns the $(LREF Date) farthest in the future which is representable 1961 by $(LREF Date). 1962 +/ 1963 @property static Date max() @safe pure nothrow @nogc 1964 { 1965 return Date(int.max / 2); 1966 } 1967 1968 private: 1969 1970 /+ 1971 Whether the given values form a valid date. 1972 1973 Params: 1974 year = The year to test. 1975 month = The month of the Gregorian Calendar to test. 1976 day = The day of the month to test. 1977 +/ 1978 static bool _valid(int year, int month, int day) @safe pure nothrow @nogc 1979 { 1980 if (!valid!"months"(month)) 1981 return false; 1982 return valid!"days"(year, month, day); 1983 } 1984 1985 1986 package: 1987 1988 /+ 1989 Adds the given number of days to this $(LREF Date). A negative number 1990 will subtract. 1991 1992 The month will be adjusted along with the day if the number of days 1993 added (or subtracted) would overflow (or underflow) the current month. 1994 The year will be adjusted along with the month if the increase (or 1995 decrease) to the month would cause it to overflow (or underflow) the 1996 current year. 1997 1998 $(D _addDays(numDays)) is effectively equivalent to 1999 $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). 2000 2001 Params: 2002 days = The number of days to add to this Date. 2003 +/ 2004 ref Date _addDays(long days) return @safe pure nothrow @nogc 2005 { 2006 _julianDay = cast(int)(_julianDay + days); 2007 return this; 2008 } 2009 2010 version (mir_test) 2011 @safe unittest 2012 { 2013 // Test A.D. 2014 { 2015 auto date = Date(1999, 2, 28); 2016 date._addDays(1); 2017 assert(date == Date(1999, 3, 1)); 2018 date._addDays(-1); 2019 assert(date == Date(1999, 2, 28)); 2020 } 2021 2022 { 2023 auto date = Date(2000, 2, 28); 2024 date._addDays(1); 2025 assert(date == Date(2000, 2, 29)); 2026 date._addDays(1); 2027 assert(date == Date(2000, 3, 1)); 2028 date._addDays(-1); 2029 assert(date == Date(2000, 2, 29)); 2030 } 2031 2032 { 2033 auto date = Date(1999, 6, 30); 2034 date._addDays(1); 2035 assert(date == Date(1999, 7, 1)); 2036 date._addDays(-1); 2037 assert(date == Date(1999, 6, 30)); 2038 } 2039 2040 { 2041 auto date = Date(1999, 7, 31); 2042 date._addDays(1); 2043 assert(date == Date(1999, 8, 1)); 2044 date._addDays(-1); 2045 assert(date == Date(1999, 7, 31)); 2046 } 2047 2048 { 2049 auto date = Date(1999, 1, 1); 2050 date._addDays(-1); 2051 assert(date == Date(1998, 12, 31)); 2052 date._addDays(1); 2053 assert(date == Date(1999, 1, 1)); 2054 } 2055 2056 { 2057 auto date = Date(1999, 7, 6); 2058 date._addDays(9); 2059 assert(date == Date(1999, 7, 15)); 2060 date._addDays(-11); 2061 assert(date == Date(1999, 7, 4)); 2062 date._addDays(30); 2063 assert(date == Date(1999, 8, 3)); 2064 date._addDays(-3); 2065 assert(date == Date(1999, 7, 31)); 2066 } 2067 2068 { 2069 auto date = Date(1999, 7, 6); 2070 date._addDays(365); 2071 assert(date == Date(2000, 7, 5)); 2072 date._addDays(-365); 2073 assert(date == Date(1999, 7, 6)); 2074 date._addDays(366); 2075 assert(date == Date(2000, 7, 6)); 2076 date._addDays(730); 2077 assert(date == Date(2002, 7, 6)); 2078 date._addDays(-1096); 2079 assert(date == Date(1999, 7, 6)); 2080 } 2081 2082 // Test B.C. 2083 { 2084 auto date = Date(-1999, 2, 28); 2085 date._addDays(1); 2086 assert(date == Date(-1999, 3, 1)); 2087 date._addDays(-1); 2088 assert(date == Date(-1999, 2, 28)); 2089 } 2090 2091 { 2092 auto date = Date(-2000, 2, 28); 2093 date._addDays(1); 2094 assert(date == Date(-2000, 2, 29)); 2095 date._addDays(1); 2096 assert(date == Date(-2000, 3, 1)); 2097 date._addDays(-1); 2098 assert(date == Date(-2000, 2, 29)); 2099 } 2100 2101 { 2102 auto date = Date(-1999, 6, 30); 2103 date._addDays(1); 2104 assert(date == Date(-1999, 7, 1)); 2105 date._addDays(-1); 2106 assert(date == Date(-1999, 6, 30)); 2107 } 2108 2109 { 2110 auto date = Date(-1999, 7, 31); 2111 date._addDays(1); 2112 assert(date == Date(-1999, 8, 1)); 2113 date._addDays(-1); 2114 assert(date == Date(-1999, 7, 31)); 2115 } 2116 2117 { 2118 auto date = Date(-1999, 1, 1); 2119 date._addDays(-1); 2120 assert(date == Date(-2000, 12, 31)); 2121 date._addDays(1); 2122 assert(date == Date(-1999, 1, 1)); 2123 } 2124 2125 { 2126 auto date = Date(-1999, 7, 6); 2127 date._addDays(9); 2128 assert(date == Date(-1999, 7, 15)); 2129 date._addDays(-11); 2130 assert(date == Date(-1999, 7, 4)); 2131 date._addDays(30); 2132 assert(date == Date(-1999, 8, 3)); 2133 date._addDays(-3); 2134 } 2135 2136 { 2137 auto date = Date(-1999, 7, 6); 2138 date._addDays(365); 2139 assert(date == Date(-1998, 7, 6)); 2140 date._addDays(-365); 2141 assert(date == Date(-1999, 7, 6)); 2142 date._addDays(366); 2143 assert(date == Date(-1998, 7, 7)); 2144 date._addDays(730); 2145 assert(date == Date(-1996, 7, 6)); 2146 date._addDays(-1096); 2147 assert(date == Date(-1999, 7, 6)); 2148 } 2149 2150 // Test Both 2151 { 2152 auto date = Date(1, 7, 6); 2153 date._addDays(-365); 2154 assert(date == Date(0, 7, 6)); 2155 date._addDays(365); 2156 assert(date == Date(1, 7, 6)); 2157 date._addDays(-731); 2158 assert(date == Date(-1, 7, 6)); 2159 date._addDays(730); 2160 assert(date == Date(1, 7, 5)); 2161 } 2162 2163 const cdate = Date(1999, 7, 6); 2164 immutable idate = Date(1999, 7, 6); 2165 static assert(!__traits(compiles, cdate._addDays(12))); 2166 static assert(!__traits(compiles, idate._addDays(12))); 2167 } 2168 2169 int _julianDay; 2170 } 2171 2172 /// ditto 2173 alias Date = date; 2174 2175 /++ 2176 Returns the number of days from the current day of the week to the given 2177 day of the week. If they are the same, then the result is 0. 2178 Params: 2179 currDoW = The current day of the week. 2180 dow = The day of the week to get the number of days to. 2181 +/ 2182 int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow @nogc 2183 { 2184 if (currDoW == dow) 2185 return 0; 2186 if (currDoW < dow) 2187 return dow - currDoW; 2188 return DayOfWeek.sun - currDoW + dow + 1; 2189 } 2190 2191 /// 2192 version (mir_test) 2193 @safe pure nothrow @nogc unittest 2194 { 2195 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); 2196 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); 2197 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); 2198 } 2199 2200 version (mir_test) 2201 @safe unittest 2202 { 2203 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); 2204 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); 2205 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); 2206 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); 2207 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); 2208 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); 2209 assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); 2210 2211 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); 2212 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); 2213 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); 2214 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); 2215 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); 2216 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); 2217 assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); 2218 2219 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); 2220 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); 2221 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); 2222 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); 2223 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); 2224 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); 2225 assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); 2226 2227 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); 2228 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); 2229 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); 2230 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); 2231 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); 2232 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); 2233 assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); 2234 2235 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); 2236 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); 2237 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); 2238 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); 2239 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); 2240 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); 2241 assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); 2242 2243 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); 2244 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); 2245 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); 2246 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); 2247 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); 2248 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); 2249 assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); 2250 2251 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); 2252 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); 2253 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); 2254 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); 2255 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); 2256 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); 2257 assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); 2258 } 2259 2260 package: 2261 2262 2263 /+ 2264 Array of the short (three letter) names of each month. 2265 +/ 2266 immutable string[12] _monthNames = ["Jan", 2267 "Feb", 2268 "Mar", 2269 "Apr", 2270 "May", 2271 "Jun", 2272 "Jul", 2273 "Aug", 2274 "Sep", 2275 "Oct", 2276 "Nov", 2277 "Dec"]; 2278 2279 /++ 2280 The maximum valid Day in the given month in the given year. 2281 2282 Params: 2283 year = The year to get the day for. 2284 month = The month of the Gregorian Calendar to get the day for. 2285 +/ 2286 public ubyte maxDay(int year, int month) @safe pure nothrow @nogc 2287 in 2288 { 2289 assert(valid!"months"(month)); 2290 } 2291 do 2292 { 2293 switch (month) 2294 { 2295 case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: 2296 return 31; 2297 case Month.feb: 2298 return yearIsLeapYear(year) ? 29 : 28; 2299 case Month.apr, Month.jun, Month.sep, Month.nov: 2300 return 30; 2301 default: 2302 assert(0, "Invalid month."); 2303 } 2304 } 2305 2306 version (mir_test) 2307 @safe unittest 2308 { 2309 // Test A.D. 2310 assert(maxDay(1999, 1) == 31); 2311 assert(maxDay(1999, 2) == 28); 2312 assert(maxDay(1999, 3) == 31); 2313 assert(maxDay(1999, 4) == 30); 2314 assert(maxDay(1999, 5) == 31); 2315 assert(maxDay(1999, 6) == 30); 2316 assert(maxDay(1999, 7) == 31); 2317 assert(maxDay(1999, 8) == 31); 2318 assert(maxDay(1999, 9) == 30); 2319 assert(maxDay(1999, 10) == 31); 2320 assert(maxDay(1999, 11) == 30); 2321 assert(maxDay(1999, 12) == 31); 2322 2323 assert(maxDay(2000, 1) == 31); 2324 assert(maxDay(2000, 2) == 29); 2325 assert(maxDay(2000, 3) == 31); 2326 assert(maxDay(2000, 4) == 30); 2327 assert(maxDay(2000, 5) == 31); 2328 assert(maxDay(2000, 6) == 30); 2329 assert(maxDay(2000, 7) == 31); 2330 assert(maxDay(2000, 8) == 31); 2331 assert(maxDay(2000, 9) == 30); 2332 assert(maxDay(2000, 10) == 31); 2333 assert(maxDay(2000, 11) == 30); 2334 assert(maxDay(2000, 12) == 31); 2335 2336 // Test B.C. 2337 assert(maxDay(-1999, 1) == 31); 2338 assert(maxDay(-1999, 2) == 28); 2339 assert(maxDay(-1999, 3) == 31); 2340 assert(maxDay(-1999, 4) == 30); 2341 assert(maxDay(-1999, 5) == 31); 2342 assert(maxDay(-1999, 6) == 30); 2343 assert(maxDay(-1999, 7) == 31); 2344 assert(maxDay(-1999, 8) == 31); 2345 assert(maxDay(-1999, 9) == 30); 2346 assert(maxDay(-1999, 10) == 31); 2347 assert(maxDay(-1999, 11) == 30); 2348 assert(maxDay(-1999, 12) == 31); 2349 2350 assert(maxDay(-2000, 1) == 31); 2351 assert(maxDay(-2000, 2) == 29); 2352 assert(maxDay(-2000, 3) == 31); 2353 assert(maxDay(-2000, 4) == 30); 2354 assert(maxDay(-2000, 5) == 31); 2355 assert(maxDay(-2000, 6) == 30); 2356 assert(maxDay(-2000, 7) == 31); 2357 assert(maxDay(-2000, 8) == 31); 2358 assert(maxDay(-2000, 9) == 30); 2359 assert(maxDay(-2000, 10) == 31); 2360 assert(maxDay(-2000, 11) == 30); 2361 assert(maxDay(-2000, 12) == 31); 2362 } 2363 2364 /+ 2365 Returns the day of the week for the given day of the Gregorian/Julian Calendar. 2366 2367 Params: 2368 day = The day of the Gregorian/Julian Calendar for which to get the day of 2369 the week. 2370 +/ 2371 DayOfWeek getDayOfWeek(int day) @safe pure nothrow @nogc 2372 { 2373 // January 1st, 1 A.D. was a Monday 2374 if (day >= 0) 2375 return cast(DayOfWeek)(day % 7); 2376 else 2377 { 2378 immutable dow = cast(DayOfWeek)((day % 7) + 7); 2379 2380 if (dow == 7) 2381 return DayOfWeek.mon; 2382 else 2383 return dow; 2384 } 2385 } 2386 2387 private: 2388 2389 enum daysInYear = 365; // The number of days in a non-leap year. 2390 enum daysInLeapYear = 366; // The numbef or days in a leap year. 2391 enum daysIn4Years = daysInYear * 3 + daysInLeapYear; // Number of days in 4 years. 2392 enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. 2393 enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. 2394 2395 /+ 2396 Array of integers representing the last days of each month in a year. 2397 +/ 2398 immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; 2399 2400 /+ 2401 Array of integers representing the last days of each month in a leap year. 2402 +/ 2403 immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; 2404 2405 2406 /+ 2407 Returns the string representation of the given month. 2408 +/ 2409 string monthToString(Month month) @safe pure @nogc nothrow 2410 { 2411 assert(month >= Month.jan && month <= Month.dec, "Invalid month"); 2412 return _monthNames[month - Month.jan]; 2413 } 2414 2415 version (mir_test) 2416 @safe unittest 2417 { 2418 assert(monthToString(Month.jan) == "Jan"); 2419 assert(monthToString(Month.feb) == "Feb"); 2420 assert(monthToString(Month.mar) == "Mar"); 2421 assert(monthToString(Month.apr) == "Apr"); 2422 assert(monthToString(Month.may) == "May"); 2423 assert(monthToString(Month.jun) == "Jun"); 2424 assert(monthToString(Month.jul) == "Jul"); 2425 assert(monthToString(Month.aug) == "Aug"); 2426 assert(monthToString(Month.sep) == "Sep"); 2427 assert(monthToString(Month.oct) == "Oct"); 2428 assert(monthToString(Month.nov) == "Nov"); 2429 assert(monthToString(Month.dec) == "Dec"); 2430 } 2431 2432 version (mir_test) 2433 version(unittest) 2434 { 2435 // All of these helper arrays are sorted in ascending order. 2436 auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; 2437 auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; 2438 2439 // I'd use a Tuple, but I get forward reference errors if I try. 2440 struct MonthDay 2441 { 2442 Month month; 2443 short day; 2444 2445 this(int m, short d) 2446 { 2447 month = cast(Month) m; 2448 day = d; 2449 } 2450 } 2451 2452 MonthDay[] testMonthDays = [MonthDay(1, 1), 2453 MonthDay(1, 2), 2454 MonthDay(3, 17), 2455 MonthDay(7, 4), 2456 MonthDay(10, 27), 2457 MonthDay(12, 30), 2458 MonthDay(12, 31)]; 2459 2460 auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; 2461 2462 Date[] testDatesBC; 2463 Date[] testDatesAD; 2464 2465 // I'd use a Tuple, but I get forward reference errors if I try. 2466 struct GregDay { int day; Date date; } 2467 auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar 2468 GregDay(-735_233, Date(-2012, 1, 1)), 2469 GregDay(-735_202, Date(-2012, 2, 1)), 2470 GregDay(-735_175, Date(-2012, 2, 28)), 2471 GregDay(-735_174, Date(-2012, 2, 29)), 2472 GregDay(-735_173, Date(-2012, 3, 1)), 2473 GregDay(-734_502, Date(-2010, 1, 1)), 2474 GregDay(-734_472, Date(-2010, 1, 31)), 2475 GregDay(-734_471, Date(-2010, 2, 1)), 2476 GregDay(-734_444, Date(-2010, 2, 28)), 2477 GregDay(-734_443, Date(-2010, 3, 1)), 2478 GregDay(-734_413, Date(-2010, 3, 31)), 2479 GregDay(-734_412, Date(-2010, 4, 1)), 2480 GregDay(-734_383, Date(-2010, 4, 30)), 2481 GregDay(-734_382, Date(-2010, 5, 1)), 2482 GregDay(-734_352, Date(-2010, 5, 31)), 2483 GregDay(-734_351, Date(-2010, 6, 1)), 2484 GregDay(-734_322, Date(-2010, 6, 30)), 2485 GregDay(-734_321, Date(-2010, 7, 1)), 2486 GregDay(-734_291, Date(-2010, 7, 31)), 2487 GregDay(-734_290, Date(-2010, 8, 1)), 2488 GregDay(-734_260, Date(-2010, 8, 31)), 2489 GregDay(-734_259, Date(-2010, 9, 1)), 2490 GregDay(-734_230, Date(-2010, 9, 30)), 2491 GregDay(-734_229, Date(-2010, 10, 1)), 2492 GregDay(-734_199, Date(-2010, 10, 31)), 2493 GregDay(-734_198, Date(-2010, 11, 1)), 2494 GregDay(-734_169, Date(-2010, 11, 30)), 2495 GregDay(-734_168, Date(-2010, 12, 1)), 2496 GregDay(-734_139, Date(-2010, 12, 30)), 2497 GregDay(-734_138, Date(-2010, 12, 31)), 2498 GregDay(-731_215, Date(-2001, 1, 1)), 2499 GregDay(-730_850, Date(-2000, 1, 1)), 2500 GregDay(-730_849, Date(-2000, 1, 2)), 2501 GregDay(-730_486, Date(-2000, 12, 30)), 2502 GregDay(-730_485, Date(-2000, 12, 31)), 2503 GregDay(-730_484, Date(-1999, 1, 1)), 2504 GregDay(-694_690, Date(-1901, 1, 1)), 2505 GregDay(-694_325, Date(-1900, 1, 1)), 2506 GregDay(-585_118, Date(-1601, 1, 1)), 2507 GregDay(-584_753, Date(-1600, 1, 1)), 2508 GregDay(-584_388, Date(-1600, 12, 31)), 2509 GregDay(-584_387, Date(-1599, 1, 1)), 2510 GregDay(-365_972, Date(-1001, 1, 1)), 2511 GregDay(-365_607, Date(-1000, 1, 1)), 2512 GregDay(-183_351, Date(-501, 1, 1)), 2513 GregDay(-182_986, Date(-500, 1, 1)), 2514 GregDay(-182_621, Date(-499, 1, 1)), 2515 GregDay(-146_827, Date(-401, 1, 1)), 2516 GregDay(-146_462, Date(-400, 1, 1)), 2517 GregDay(-146_097, Date(-400, 12, 31)), 2518 GregDay(-110_302, Date(-301, 1, 1)), 2519 GregDay(-109_937, Date(-300, 1, 1)), 2520 GregDay(-73_778, Date(-201, 1, 1)), 2521 GregDay(-73_413, Date(-200, 1, 1)), 2522 GregDay(-38_715, Date(-105, 1, 1)), 2523 GregDay(-37_254, Date(-101, 1, 1)), 2524 GregDay(-36_889, Date(-100, 1, 1)), 2525 GregDay(-36_524, Date(-99, 1, 1)), 2526 GregDay(-36_160, Date(-99, 12, 31)), 2527 GregDay(-35_794, Date(-97, 1, 1)), 2528 GregDay(-18_627, Date(-50, 1, 1)), 2529 GregDay(-18_262, Date(-49, 1, 1)), 2530 GregDay(-3652, Date(-9, 1, 1)), 2531 GregDay(-2191, Date(-5, 1, 1)), 2532 GregDay(-1827, Date(-5, 12, 31)), 2533 GregDay(-1826, Date(-4, 1, 1)), 2534 GregDay(-1825, Date(-4, 1, 2)), 2535 GregDay(-1462, Date(-4, 12, 30)), 2536 GregDay(-1461, Date(-4, 12, 31)), 2537 GregDay(-1460, Date(-3, 1, 1)), 2538 GregDay(-1096, Date(-3, 12, 31)), 2539 GregDay(-1095, Date(-2, 1, 1)), 2540 GregDay(-731, Date(-2, 12, 31)), 2541 GregDay(-730, Date(-1, 1, 1)), 2542 GregDay(-367, Date(-1, 12, 30)), 2543 GregDay(-366, Date(-1, 12, 31)), 2544 GregDay(-365, Date(0, 1, 1)), 2545 GregDay(-31, Date(0, 11, 30)), 2546 GregDay(-30, Date(0, 12, 1)), 2547 GregDay(-1, Date(0, 12, 30)), 2548 GregDay(0, Date(0, 12, 31))]; 2549 2550 auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), 2551 GregDay(2, Date(1, 1, 2)), 2552 GregDay(32, Date(1, 2, 1)), 2553 GregDay(365, Date(1, 12, 31)), 2554 GregDay(366, Date(2, 1, 1)), 2555 GregDay(731, Date(3, 1, 1)), 2556 GregDay(1096, Date(4, 1, 1)), 2557 GregDay(1097, Date(4, 1, 2)), 2558 GregDay(1460, Date(4, 12, 30)), 2559 GregDay(1461, Date(4, 12, 31)), 2560 GregDay(1462, Date(5, 1, 1)), 2561 GregDay(17_898, Date(50, 1, 1)), 2562 GregDay(35_065, Date(97, 1, 1)), 2563 GregDay(36_160, Date(100, 1, 1)), 2564 GregDay(36_525, Date(101, 1, 1)), 2565 GregDay(37_986, Date(105, 1, 1)), 2566 GregDay(72_684, Date(200, 1, 1)), 2567 GregDay(73_049, Date(201, 1, 1)), 2568 GregDay(109_208, Date(300, 1, 1)), 2569 GregDay(109_573, Date(301, 1, 1)), 2570 GregDay(145_732, Date(400, 1, 1)), 2571 GregDay(146_098, Date(401, 1, 1)), 2572 GregDay(182_257, Date(500, 1, 1)), 2573 GregDay(182_622, Date(501, 1, 1)), 2574 GregDay(364_878, Date(1000, 1, 1)), 2575 GregDay(365_243, Date(1001, 1, 1)), 2576 GregDay(584_023, Date(1600, 1, 1)), 2577 GregDay(584_389, Date(1601, 1, 1)), 2578 GregDay(693_596, Date(1900, 1, 1)), 2579 GregDay(693_961, Date(1901, 1, 1)), 2580 GregDay(729_755, Date(1999, 1, 1)), 2581 GregDay(730_120, Date(2000, 1, 1)), 2582 GregDay(730_121, Date(2000, 1, 2)), 2583 GregDay(730_484, Date(2000, 12, 30)), 2584 GregDay(730_485, Date(2000, 12, 31)), 2585 GregDay(730_486, Date(2001, 1, 1)), 2586 GregDay(733_773, Date(2010, 1, 1)), 2587 GregDay(733_774, Date(2010, 1, 2)), 2588 GregDay(733_803, Date(2010, 1, 31)), 2589 GregDay(733_804, Date(2010, 2, 1)), 2590 GregDay(733_831, Date(2010, 2, 28)), 2591 GregDay(733_832, Date(2010, 3, 1)), 2592 GregDay(733_862, Date(2010, 3, 31)), 2593 GregDay(733_863, Date(2010, 4, 1)), 2594 GregDay(733_892, Date(2010, 4, 30)), 2595 GregDay(733_893, Date(2010, 5, 1)), 2596 GregDay(733_923, Date(2010, 5, 31)), 2597 GregDay(733_924, Date(2010, 6, 1)), 2598 GregDay(733_953, Date(2010, 6, 30)), 2599 GregDay(733_954, Date(2010, 7, 1)), 2600 GregDay(733_984, Date(2010, 7, 31)), 2601 GregDay(733_985, Date(2010, 8, 1)), 2602 GregDay(734_015, Date(2010, 8, 31)), 2603 GregDay(734_016, Date(2010, 9, 1)), 2604 GregDay(734_045, Date(2010, 9, 30)), 2605 GregDay(734_046, Date(2010, 10, 1)), 2606 GregDay(734_076, Date(2010, 10, 31)), 2607 GregDay(734_077, Date(2010, 11, 1)), 2608 GregDay(734_106, Date(2010, 11, 30)), 2609 GregDay(734_107, Date(2010, 12, 1)), 2610 GregDay(734_136, Date(2010, 12, 30)), 2611 GregDay(734_137, Date(2010, 12, 31)), 2612 GregDay(734_503, Date(2012, 1, 1)), 2613 GregDay(734_534, Date(2012, 2, 1)), 2614 GregDay(734_561, Date(2012, 2, 28)), 2615 GregDay(734_562, Date(2012, 2, 29)), 2616 GregDay(734_563, Date(2012, 3, 1)), 2617 GregDay(734_858, Date(2012, 12, 21))]; 2618 2619 // I'd use a Tuple, but I get forward reference errors if I try. 2620 struct DayOfYear { int day; MonthDay md; } 2621 auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), 2622 DayOfYear(2, MonthDay(1, 2)), 2623 DayOfYear(3, MonthDay(1, 3)), 2624 DayOfYear(31, MonthDay(1, 31)), 2625 DayOfYear(32, MonthDay(2, 1)), 2626 DayOfYear(59, MonthDay(2, 28)), 2627 DayOfYear(60, MonthDay(3, 1)), 2628 DayOfYear(90, MonthDay(3, 31)), 2629 DayOfYear(91, MonthDay(4, 1)), 2630 DayOfYear(120, MonthDay(4, 30)), 2631 DayOfYear(121, MonthDay(5, 1)), 2632 DayOfYear(151, MonthDay(5, 31)), 2633 DayOfYear(152, MonthDay(6, 1)), 2634 DayOfYear(181, MonthDay(6, 30)), 2635 DayOfYear(182, MonthDay(7, 1)), 2636 DayOfYear(212, MonthDay(7, 31)), 2637 DayOfYear(213, MonthDay(8, 1)), 2638 DayOfYear(243, MonthDay(8, 31)), 2639 DayOfYear(244, MonthDay(9, 1)), 2640 DayOfYear(273, MonthDay(9, 30)), 2641 DayOfYear(274, MonthDay(10, 1)), 2642 DayOfYear(304, MonthDay(10, 31)), 2643 DayOfYear(305, MonthDay(11, 1)), 2644 DayOfYear(334, MonthDay(11, 30)), 2645 DayOfYear(335, MonthDay(12, 1)), 2646 DayOfYear(363, MonthDay(12, 29)), 2647 DayOfYear(364, MonthDay(12, 30)), 2648 DayOfYear(365, MonthDay(12, 31))]; 2649 2650 auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), 2651 DayOfYear(2, MonthDay(1, 2)), 2652 DayOfYear(3, MonthDay(1, 3)), 2653 DayOfYear(31, MonthDay(1, 31)), 2654 DayOfYear(32, MonthDay(2, 1)), 2655 DayOfYear(59, MonthDay(2, 28)), 2656 DayOfYear(60, MonthDay(2, 29)), 2657 DayOfYear(61, MonthDay(3, 1)), 2658 DayOfYear(91, MonthDay(3, 31)), 2659 DayOfYear(92, MonthDay(4, 1)), 2660 DayOfYear(121, MonthDay(4, 30)), 2661 DayOfYear(122, MonthDay(5, 1)), 2662 DayOfYear(152, MonthDay(5, 31)), 2663 DayOfYear(153, MonthDay(6, 1)), 2664 DayOfYear(182, MonthDay(6, 30)), 2665 DayOfYear(183, MonthDay(7, 1)), 2666 DayOfYear(213, MonthDay(7, 31)), 2667 DayOfYear(214, MonthDay(8, 1)), 2668 DayOfYear(244, MonthDay(8, 31)), 2669 DayOfYear(245, MonthDay(9, 1)), 2670 DayOfYear(274, MonthDay(9, 30)), 2671 DayOfYear(275, MonthDay(10, 1)), 2672 DayOfYear(305, MonthDay(10, 31)), 2673 DayOfYear(306, MonthDay(11, 1)), 2674 DayOfYear(335, MonthDay(11, 30)), 2675 DayOfYear(336, MonthDay(12, 1)), 2676 DayOfYear(364, MonthDay(12, 29)), 2677 DayOfYear(365, MonthDay(12, 30)), 2678 DayOfYear(366, MonthDay(12, 31))]; 2679 2680 void initializeTests() @safe 2681 { 2682 foreach (year; testYearsBC) 2683 { 2684 foreach (md; testMonthDays) 2685 testDatesBC ~= Date(year, md.month, md.day); 2686 } 2687 2688 foreach (year; testYearsAD) 2689 { 2690 foreach (md; testMonthDays) 2691 testDatesAD ~= Date(year, md.month, md.day); 2692 } 2693 } 2694 }