1 /******************************************************************************* 2 3 Copyright: Copyright (c) 2007-2008 Matti Niemenmaa. 4 All rights reserved 5 License: BSD style: $(LICENSE) 6 Version: Aug 2007: Initial release 7 Feb 2008: Retooled 8 Author: Matti Niemenmaa 9 10 This module is based on the ISO 8601:2004 standard, and has functions 11 for parsing (almost) every date/time format specified therein. (The 12 ones not supported are intervals, durations, and recurring intervals.) 13 14 Refer to the standard for a full description of the formats supported. 15 16 The functions (parseTime, parseDate, and parseDateAndTime) are 17 overloaded into two different versions of each: one updates a given 18 Time, and the other updates a given ExtendedDate struct. The purpose of 19 this struct is to support more detailed information which the Time data 20 type does not (and, given its simple integer nature, cannot) support. 21 22 Times with specified time zones are simply converted into UTC: this may 23 lead to the date changing when only a time was parsed: e.g. "01:00+03" 24 is the same as "22:00", except that when the former is parsed, one is 25 subtracted from the day. 26 27 *******************************************************************************/ 28 29 module tango.time.ISO8601; 30 31 public import tango.time.Time; 32 public import tango.time.chrono.Gregorian; 33 34 import tango.core.Exception : IllegalArgumentException; 35 import tango.math.Math : min; 36 37 private alias Time DT; 38 private alias ExtendedDate FullDate; 39 40 /** An extended date type, wrapping a Time together with some additional 41 * information. */ 42 public struct ExtendedDate { 43 /** The Time value, containing the information it can. */ 44 DT val; 45 46 private int year_; 47 48 /** Returns the year part of the date: a value in the range 49 * [-1_000_000_000,-1] ∪ [1,999_999_999], where -1 is the year 1 BCE. 50 * 51 * Do not use val.year directly unless you are absolutely sure that it is in 52 * the range a Time can hold (-10000 to 9999). 53 */ 54 @property const int year() 55 out(val) { 56 assert ( (val >= -1_000_000_000 && val <= -1) 57 || (val >= 1 && val <= 999_999_999)); 58 } body { 59 if (year_) 60 return year_; 61 62 auto era = Gregorian.generic.getEra(val); 63 if (era == Gregorian.AD_ERA) 64 return Gregorian.generic.getYear(val); 65 else 66 return -Gregorian.generic.getYear(val); 67 } 68 69 // y may be zero: if so, it refers to the year 1 BCE 70 @property private void year(int y) { 71 if (DTyear(y)) { 72 year_ = 0; 73 // getYear returns uint: be careful with promotion to unsigned 74 int toAdd = y - Gregorian.generic.getYear(val); 75 val = Gregorian.generic.addYears(val, toAdd); 76 } else 77 year_ = y < 0 ? y-1 : y; 78 } 79 80 private byte mask; // leap second and endofday 81 82 /** Returns the seconds part of the date: may be 60 if a leap second 83 * occurred. In such a case, val's seconds part is 59. 84 */ 85 @property const uint seconds() { return val.time.seconds + ((mask >>> 0) & 1); } 86 alias seconds secs, second, sec; 87 88 /** Whether the ISO 8601 representation of this hour is 24 or 00: whether 89 * this instant of midnight is to be considered the end of the previous day 90 * or the start of the next. 91 * 92 * If the time of val is not exactly 00:00:00.000, this value is undefined. 93 */ 94 @property const bool endOfDay() { return 1 == ((mask >>> 1) & 1); } 95 96 private void setLeap () { mask |= 1 << 0; } 97 private void setEndOfDay() { mask |= 1 << 1; } 98 99 debug (Tango_ISO8601) private char[] toStr() { 100 return Stdout.layout.convert( 101 "{:d} and {:d}-{:d2}-{:d2} :: {:d2}:{:d2}:{:d2}.{:d3} and {:d2}, {}", 102 year_, years(*this), months(*this), days(*this), 103 hours(*this), mins(*this), .secs(*this), ms(*this), 104 this.seconds, this.endOfDay); 105 } 106 } 107 108 /** Parses a date in a format specified in ISO 8601:2004. 109 * 110 * Returns the number of characters used to compose a valid date: 0 if no date 111 * can be composed. 112 * 113 * Fields in dt will either be correct (e.g. months will be >= 1 and <= 12) or 114 * the default, which is 1 for year, month, and day, and 0 for all other 115 * fields. Unless one is absolutely sure that 0001-01-01 can never be 116 * encountered, one should check the return value to be sure that the parsing 117 * succeeded as expected. 118 * 119 * A third parameter is available for the ExtendedDate version: this allows for 120 * parsing expanded year representations. The parameter is the number of extra 121 * year digits beyond four, and defaults to zero. It must be within the range 122 * [0,5]: this allows for a maximum year of 999 999 999, which should be enough 123 * for now. 124 * 125 * When using expanded year representations, be careful to use 126 * ExtendedDate.year instead of the Time's year value. 127 * 128 * Examples: 129 * --- 130 * Time t; 131 * ExtendedDate ed; 132 * 133 * parseDate("19", t); // January 1st, 1900 134 * parseDate("1970", t); // January 1st, 1970 135 * parseDate("1970-02", t); // February 1st, 1970 136 * parseDate("19700203", t); // February 3rd, 1970 137 * parseDate("+19700203", ed, 2); // March 1st, 197002 138 * parseDate("-197002-04-01", ed, 2); // April 1st, -197003 (197003 BCE) 139 * parseDate("00000101", t); // January 1st, -1 (1 BCE) 140 * parseDate("1700-W14-2", t); // April 6th, 1700 141 * parseDate("2008W01", t); // December 31st, 2007 142 * parseDate("1987-221", t); // August 9th, 1987 143 * parseDate("1234abcd", t); // January 1st, 1234; return value is 4 144 * parseDate("12abcdef", t); // January 1st, 1200; return value is 2 145 * parseDate("abcdefgh", t); // January 1st, 0001; return value is 0 146 * --- 147 */ 148 public size_t parseDate(T)(T[] src, ref DT dt) { 149 auto fd = FullDate(dt); 150 151 auto ret = parseDate(src, fd); 152 dt = fd.val; 153 return ret; 154 } 155 /** ditto */ 156 public size_t parseDate(T)(T[] src, ref FullDate fd, ubyte expanded = 0) { 157 ubyte dummy = void; 158 T* p = src.ptr; 159 return doIso8601Date(p, src, fd, expanded, dummy); 160 } 161 162 private size_t doIso8601Date(T)( 163 164 ref T* p, T[] src, 165 ref FullDate fd, 166 ubyte expanded, 167 out ubyte separators 168 169 ) { 170 if (expanded > 5) 171 throw new IllegalArgumentException( 172 "ISO8601 :: year expanded by more than 5 digits does not fit in int"); 173 174 size_t eaten() { return p - src.ptr; } 175 size_t remaining() { return src.length - eaten(); } 176 bool done(const(T[]) s) { return .done(eaten(), src.length, p, s); } 177 178 if (!parseYear(p, src.length, expanded, fd)) 179 return 0; 180 181 auto onlyYear = eaten(); 182 183 // /([+-]Y{expanded})?(YYYY|YY)/ 184 if (done("-0123W")) 185 return onlyYear; 186 187 if (accept(p, '-')) { 188 separators = YES; 189 190 if (remaining() == 0) 191 return eaten() - 1; 192 } 193 194 if (accept(p, 'W')) { 195 T* p2 = p; 196 197 int i = parseInt(p, min(cast(size_t)3, remaining())); 198 199 if (i) 200 { 201 if (p - p2 == 2) { 202 203 // (year)-Www 204 if (done("-")) { 205 if (getMonthAndDayFromWeek(fd, i)) 206 return eaten(); 207 208 // (year)-Www-D 209 } else if (demand(p, '-')) { 210 if (remaining() == 0) 211 return eaten() - 1; 212 213 if (separators == NO) { 214 // (year)Www after all 215 if (getMonthAndDayFromWeek(fd, i)) 216 return eaten() - 1; 217 218 } else if (getMonthAndDayFromWeek(fd, i, *p++ - '0')) 219 return eaten(); 220 } 221 222 } else if (p - p2 == 3) { 223 // (year)WwwD, i == wwD 224 225 if (separators == YES) { 226 // (year)-Www after all 227 if (getMonthAndDayFromWeek(fd, i / 10)) 228 return eaten() - 1; 229 230 } else if (getMonthAndDayFromWeek(fd, i / 10, i % 10)) 231 return eaten(); 232 } 233 } 234 return onlyYear; 235 } 236 237 // next up, MM or MM[-]DD or DDD 238 239 T* p2 = p; 240 241 int i = parseInt(p, remaining()); 242 if (!i) 243 return onlyYear; 244 245 switch (p - p2) { 246 case 2: 247 // MM or MM-DD 248 249 if (i >= 1 && i <= 12) 250 addMonths(fd, i); 251 else 252 return onlyYear; 253 254 auto onlyMonth = eaten(); 255 256 // (year)-MM 257 if (done("-") || !demand(p, '-') || separators == NO) 258 return onlyMonth; 259 260 int day = parseInt(p, min(cast(size_t)2, remaining())); 261 262 // (year)-MM-DD 263 if (day && day <= daysPerMonth(months(fd), fd.year)) 264 addDays(fd, day); 265 else 266 return onlyMonth; 267 268 break; 269 270 case 4: 271 // e.g. 20010203, i = 203 now 272 273 int month = i / 100; 274 int day = i % 100; 275 276 if (separators == YES) { 277 // Don't accept the day: behave as though we only got the month 278 p -= 2; 279 i = month; 280 goto case 2; 281 } 282 283 // (year)MMDD 284 if ( 285 month >= 1 && month <= 12 && 286 day >= 1 && day <= daysPerMonth(month, fd.year) 287 ) { 288 addMonths(fd, month); 289 addDays (fd, day); 290 } else 291 return onlyYear; 292 293 break; 294 295 case 3: 296 // (year)-DDD 297 // i is the ordinal of the day within the year 298 299 if (i > 365 + isLeapYear(fd.year)) 300 return onlyYear; 301 302 addDays(fd, i); 303 break; 304 default: break; 305 } 306 return eaten(); 307 } 308 309 /** Parses a time of day in a format specified in ISO 8601:2004. 310 * 311 * Returns the number of characters used to compose a valid time: 0 if no time 312 * can be composed. 313 * 314 * Fields in dt will either be correct or the default, which is 0 for all 315 * time-related fields. fields. Unless one is absolutely sure that midnight 316 * can never be encountered, one should check the return value to be sure that 317 * the parsing succeeded as expected. 318 * 319 * Extra fields in ExtendedDate: 320 * 321 * Seconds may be 60 if the hours and minutes are 23 and 59, as leap seconds 322 * are occasionally added to UTC time. A Time's seconds will be 59 in this 323 * case. 324 * 325 * Hours may be 0 or 24: the latter marks the end of a day and the former the 326 * beginning, although they both refer to the same instant in time. A Time 327 * will be precisely 00:00 in either case. 328 * 329 * Examples: 330 * --- 331 * Time t; 332 * ExtendedDate ed; 333 * 334 * // ",000" omitted for clarity 335 * parseTime("20", t); // 20:00:00 336 * parseTime("2004", t); // 20:04:00 337 * parseTime("20:04:06", t); // 20:04:06 338 * parseTime("16:49:30,001", t); // 16:49:30,001 339 * parseTime("16:49:30,1", t); // 16:49:30,100 340 * parseTime("16:49,4", t); // 16:49:24 341 * parseTime("23:59:60", ed); // 23:59:60 342 * parseTime("24:00:01", t); // 00:00:00; return value is 5 343 * parseTime("24:00:01", ed); // 00:00:00; return value is 5; endOfDay 344 * parseTime("30", t); // 00:00:00; return value is 0 345 * parseTime("21:32:43-12:34", t); // 10:06:43; day increased by one 346 * --- 347 */ 348 public size_t parseTime(T)(T[] src, ref DT dt) { 349 auto fd = FullDate(dt); 350 351 auto ret = parseTime(src, fd); 352 dt = fd.val; 353 return ret; 354 } 355 /** ditto */ 356 public size_t parseTime(T)(T[] src, ref FullDate fd) { 357 bool dummy = void; 358 T* p = src.ptr; 359 return doIso8601Time(p, src, fd, WHATEVER, dummy); 360 } 361 362 // separators 363 private enum : ubyte { NO = 0, YES = 1, WHATEVER } 364 365 // bothValid is used only to get parseDateAndTime() to catch errors correctly 366 private size_t doIso8601Time(T)( 367 368 ref T* p, T[] src, 369 ref FullDate fd, 370 ubyte separators, 371 out bool bothValid 372 373 ) { 374 size_t eaten() { return p - src.ptr; } 375 size_t remaining() { return src.length - eaten(); } 376 bool done(const(T[]) s) { return .done(eaten(), src.length, p, s); } 377 bool checkColon() { return .checkColon(p, separators); } 378 379 byte getTimeZone() { return .getTimeZone(p, remaining(), fd, separators, &done); } 380 381 if (separators == WHATEVER) 382 accept(p, 'T'); 383 384 int hour = void; 385 if (parseInt(p, min(cast(size_t)2, remaining()), hour) != 2 || hour > 24) 386 return 0; 387 388 if (hour == 24) 389 fd.setEndOfDay(); 390 391 // Add the hours even if endOfDay: the day should be the next day, not the 392 // previous 393 addHours(fd, hour); 394 395 auto onlyHour = eaten(); 396 397 // hh 398 if (done("+,-.012345:")) 399 return onlyHour; 400 401 switch (getDecimal(p, remaining(), fd, HOUR)) { 402 case NOTFOUND: break; 403 case FOUND: 404 auto onlyDecimal = eaten(); 405 if (getTimeZone() == BAD) 406 return onlyDecimal; 407 408 // /hh,h+/ 409 return eaten(); 410 411 case BAD: return onlyHour; 412 default: assert (false); 413 } 414 415 switch (getTimeZone()) { 416 case NOTFOUND: break; 417 case FOUND: return eaten(); 418 case BAD: return onlyHour; 419 default: assert (false); 420 } 421 422 if (!checkColon()) 423 return onlyHour; 424 425 int mins = void; 426 if ( 427 parseInt(p, min(cast(size_t)2, remaining()), mins) != 2 || 428 mins > 59 || 429 // end of day is only for 24:00:00 430 (fd.endOfDay && mins != 0) 431 ) 432 return onlyHour; 433 434 addMins(fd, mins); 435 436 auto onlyMinute = eaten(); 437 438 // hh:mm 439 if (done("+,-.0123456:")) { 440 bothValid = true; 441 return onlyMinute; 442 } 443 444 switch (getDecimal(p, remaining(), fd, MINUTE)) { 445 case NOTFOUND: break; 446 case FOUND: 447 auto onlyDecimal = eaten(); 448 if (getTimeZone() == BAD) 449 return onlyDecimal; 450 451 // /hh:mm,m+/ 452 bothValid = true; 453 return eaten(); 454 455 case BAD: return onlyMinute; 456 default: assert (false); 457 } 458 459 switch (getTimeZone()) { 460 case NOTFOUND: break; 461 case FOUND: bothValid = true; return eaten(); 462 case BAD: return onlyMinute; 463 default: assert (false); 464 } 465 466 if (!checkColon()) 467 return onlyMinute; 468 469 int sec = void; 470 if ( 471 parseInt(p, min(cast(size_t)2, remaining()), sec) != 2 || 472 sec > 60 || 473 (fd.endOfDay && sec != 0) 474 ) 475 return onlyMinute; 476 477 if (sec == 60) { 478 if (hours(fd) != 23 && .mins(fd) != 59) 479 return onlyMinute; 480 481 fd.setLeap(); 482 --sec; 483 } 484 addSecs(fd, sec); 485 486 auto onlySecond = eaten(); 487 488 // hh:mm:ss 489 if (done("+,-.Z")) { 490 bothValid = true; 491 return onlySecond; 492 } 493 494 switch (getDecimal(p, remaining(), fd, SECOND)) { 495 case NOTFOUND: break; 496 case FOUND: 497 auto onlyDecimal = eaten(); 498 if (getTimeZone() == BAD) 499 return onlyDecimal; 500 501 // /hh:mm:ss,s+/ 502 bothValid = true; 503 return eaten(); 504 505 case BAD: return onlySecond; 506 default: assert (false); 507 } 508 509 if (getTimeZone() == BAD) 510 return onlySecond; 511 else { 512 bothValid = true; 513 return eaten(); // hh:mm:ss with timezone 514 } 515 } 516 517 /** Parses a combined date and time in a format specified in ISO 8601:2004. 518 * 519 * Returns the number of characters used to compose a valid date and time. 520 * Zero is returned if a complete date and time cannot be extracted. In that 521 * case, the value of the resulting Time or ExtendedDate is undefined. 522 * 523 * This function is stricter than just calling parseDate followed by 524 * parseTime: there are no allowances for expanded years or reduced dates 525 * (two-digit years), and separator usage must be consistent. 526 * 527 * Although the standard allows for omitting the T between the date and the 528 * time, this function requires it. 529 * 530 * Examples: 531 * --- 532 * Time t; 533 * 534 * // January 1st, 2008 00:01:00 535 * parseDateAndTime("2007-12-31T23:01-01", t); 536 * 537 * // April 12th, 1985 23:50:30,042 538 * parseDateAndTime("1985W155T235030,042", t); 539 * 540 * // Invalid time: returns zero 541 * parseDateAndTime("1902-03-04T10:1a", t); 542 * 543 * // Separating T omitted: returns zero 544 * parseDateAndTime("1985-04-1210:15:30+04:00", t); 545 * 546 * // Inconsistent separators: all return zero 547 * parseDateAndTime("200512-01T10:02", t); 548 * parseDateAndTime("1985-04-12T10:15:30+0400", t); 549 * parseDateAndTime("1902-03-04T050607", t); 550 * --- 551 */ 552 public size_t parseDateAndTime(T)(T[] src, ref DT dt) { 553 FullDate fd; 554 auto ret = parseDateAndTime(src, fd); 555 dt = fd.val; 556 return ret; 557 } 558 /** ditto */ 559 public size_t parseDateAndTime(T)(T[] src, ref FullDate fd) { 560 T* p = src.ptr; 561 ubyte sep; 562 bool bothValid = false; 563 564 if ( 565 doIso8601Date(p, src, fd, cast(ubyte)0, sep) && 566 567 // by mutual agreement this T may be omitted 568 // but this is just a convenience method for date+time anyway 569 src.length - (p - src.ptr) >= 1 && 570 demand(p, 'T') && 571 572 doIso8601Time(p, src, fd, sep, bothValid) && 573 bothValid 574 ) 575 return p - src.ptr; 576 else 577 return 0; 578 } 579 580 /+ +++++++++++++++++++++++++++++++++++++++ +\ 581 582 Privates used by date 583 584 \+ +++++++++++++++++++++++++++++++++++++++ +/ 585 586 private: 587 588 // /([+-]Y{expanded})?(YYYY|YY)/ 589 bool parseYear(T)(ref T* p, size_t len, ubyte expanded, ref FullDate fd) { 590 591 int year = void; 592 593 bool doParse() { 594 T* p2 = p; 595 596 if (!parseInt(p, min(cast(size_t)(expanded + 4), len), year)) 597 return false; 598 599 // it's Y{expanded}YY, Y{expanded}YYYY, or unacceptable 600 601 if (p - p2 - expanded == 2) 602 year *= 100; 603 else if (p - p2 - expanded != 4) 604 return false; 605 606 return true; 607 } 608 609 if (accept(p, '-')) { 610 if (!doParse() || year < 0) 611 return false; 612 year = -year; 613 } else { 614 accept(p, '+'); 615 if (!doParse() || year < 0) 616 return false; 617 } 618 619 fd.year = year; 620 621 return true; 622 } 623 624 // find the month and day given a calendar week and the day of the week 625 // uses fd.year for leap year calculations 626 // returns false if week and fd.year are incompatible 627 bool getMonthAndDayFromWeek(ref FullDate fd, int week, int day = 1) { 628 if (week < 1 || week > 53 || day < 1 || day > 7) 629 return false; 630 631 int year = fd.year; 632 633 // only years starting with Thursday and leap years starting with Wednesday 634 // have 53 weeks 635 if (week == 53) { 636 int startingDay = dayOfWeek(year, 1, 1); 637 638 if (!(startingDay == 4 || (isLeapYear(year) && startingDay == 3))) 639 return false; 640 } 641 642 // XXX 643 // days since year-01-04, plus 4 (?)... 644 /* This is a bit scary, actually: I have ***no idea why this works***. I 645 * came up with this completely by accident. It seems to work though - 646 * unless it fails in some (very) obscure case which isn't represented in 647 * the unit tests. 648 */ 649 addDays(fd, 7*(week - 1) + day - dayOfWeek(year, 1, 4) + 4); 650 651 return true; 652 } 653 654 bool isLeapYear(int year) { 655 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 656 } 657 658 int dayOfWeek(int year, int month, int day) 659 in { 660 assert (month >= 1 && month <= 12); 661 assert (day >= 1 && day <= 31); 662 } out(result) { 663 assert (result >= 1 && result <= 7); 664 } body { 665 uint era = erafy(year); 666 667 int result = 668 Gregorian.generic.getDayOfWeek( 669 Gregorian.generic.toTime(year, month, day, 0, 0, 0, 0, era)); 670 671 if (result == Gregorian.DayOfWeek.Sunday) 672 return 7; 673 else 674 return result; 675 } 676 677 /+ +++++++++++++++++++++++++++++++++++++++ +\ 678 679 Privates used by time 680 681 \+ +++++++++++++++++++++++++++++++++++++++ +/ 682 683 enum : ubyte { HOUR, MINUTE, SECOND } 684 enum : byte { BAD, FOUND, NOTFOUND } 685 686 bool checkColon(T)(ref T* p, ref ubyte separators) { 687 ubyte foundSep = accept(p, ':') ? YES : NO; 688 if (foundSep != separators) { 689 if (separators == WHATEVER) 690 separators = foundSep; 691 else 692 return false; 693 } 694 return true; 695 } 696 697 byte getDecimal(T)(ref T* p, size_t len, ref FullDate fd, ubyte which) { 698 if (!(accept(p, ',') || accept(p, '.'))) 699 return NOTFOUND; 700 701 T* p2 = p; 702 703 int i = void; 704 auto iLen = parseInt(p, len-1, i); 705 706 if ( 707 iLen == 0 || 708 709 // if i is 0, must have at least 3 digits 710 // ... or at least that's what I think the standard means 711 // when it says "[i]f the magnitude of the number is less 712 // than unity, the decimal sign shall be preceded by two 713 // zeros"... 714 // surely that should read "followed" and not "preceded" 715 716 (i == 0 && iLen < 3) 717 ) 718 return BAD; 719 720 // 10 to the power of (iLen - 1) 721 int pow = 1; 722 while (--iLen) 723 pow *= 10; 724 725 switch (which) { 726 case HOUR: 727 addMins(fd, 6 * i / pow); 728 addSecs(fd, 6 * i % pow); 729 break; 730 case MINUTE: 731 addSecs(fd, 6 * i / pow); 732 addMs (fd, 6000 * i / pow % 1000); 733 break; 734 case SECOND: 735 addMs(fd, 100 * i / pow); 736 break; 737 738 default: assert (false); 739 } 740 741 return FOUND; 742 } 743 744 // the DT is always UTC, so this just adds the offset to the date fields 745 // another option would be to add time zone fields to DT and have this fill them 746 747 byte getTimeZone(T)(ref const(T)* p, size_t len, ref FullDate fd, ubyte separators, bool delegate(const(T[])) done) { 748 bool checkColon() { return .checkColon(p, separators); } 749 750 if (len == 0) 751 return NOTFOUND; 752 753 auto p0 = p; 754 755 if (accept(p, 'Z')) 756 return FOUND; 757 758 int factor = -1; 759 760 if (accept(p, '-')) 761 factor = 1; 762 else if (!accept(p, '+')) 763 return NOTFOUND; 764 765 int hour = void; 766 if (parseInt(p, min(cast(size_t)2, len-1), hour) != 2 || hour > 12 || (hour == 0 && factor == 1)) 767 return BAD; 768 769 addHours(fd, factor * hour); 770 771 // if we go forward in time to midnight, it's 24:00 772 if ( 773 factor > 0 && 774 hours(fd) == 0 && mins(fd) == 0 && secs(fd) == 0 && ms(fd) == 0 775 ) 776 fd.setEndOfDay(); 777 778 if (done("012345:")) 779 return FOUND; 780 781 auto afterHours = p; 782 783 if (!checkColon()) 784 return BAD; 785 786 int minute = void; 787 if (parseInt(p, min(cast(size_t)2, len - (p-p0)), minute) != 2) { 788 // The hours were valid even if the minutes weren't 789 p = afterHours; 790 return FOUND; 791 } 792 793 addMins(fd, factor * minute); 794 795 // as above 796 if ( 797 factor > 0 && 798 hours(fd) == 0 && mins(fd) == 0 && secs(fd) == 0 && ms(fd) == 0 799 ) 800 fd.setEndOfDay(); 801 802 return FOUND; 803 } 804 805 /+ +++++++++++++++++++++++++++++++++++++++ +\ 806 807 Privates used by both date and time 808 809 \+ +++++++++++++++++++++++++++++++++++++++ +/ 810 811 bool accept(T)(ref T* p, char c) { 812 if (*p == c) { 813 ++p; 814 return true; 815 } 816 return false; 817 } 818 819 bool demand(T)(ref T* p, char c) { 820 return (*p++ == c); 821 } 822 823 bool done(T)(size_t eaten, size_t srcLen, const(T*) p, const(T[]) s) { 824 if (eaten == srcLen) 825 return true; 826 827 // s is the array of characters which may come next 828 // (i.e. which *p may be) 829 // sorted in ascending order 830 T t = *p; 831 foreach (c; s) { 832 if (t < c) 833 return true; 834 else if (t == c) 835 break; 836 } 837 return false; 838 } 839 840 int daysPerMonth(int month, int year) { 841 uint era = erafy(year); 842 return Gregorian.generic.getDaysInMonth(year, month, era); 843 } 844 845 uint erafy(ref int year) { 846 if (year < 0) { 847 year *= -1; 848 return Gregorian.BC_ERA; 849 } else 850 return Gregorian.AD_ERA; 851 } 852 853 /+ +++++++++++++++++++++++++++++++++++++++ +\ 854 855 Extract an integer from the input, accept no more than max digits 856 857 \+ +++++++++++++++++++++++++++++++++++++++ +/ 858 859 // note: code relies on these always being positive, failing if *p == '-' 860 861 int parseInt(T)(ref T* p, size_t max) { 862 size_t i = 0; 863 int value = 0; 864 while (i < max && p[i] >= '0' && p[i] <= '9') 865 value = value * 10 + p[i++] - '0'; 866 p += i; 867 return value; 868 } 869 870 // ... and return the amount of digits processed 871 872 size_t parseInt(T)(ref T* p, size_t max, out int i) { 873 T* p2 = p; 874 i = parseInt(p, max); 875 return p - p2; 876 } 877 878 879 /+ +++++++++++++++++++++++++++++++++++++++ +\ 880 881 Helpers for DT/FullDate manipulation 882 883 \+ +++++++++++++++++++++++++++++++++++++++ +/ 884 885 // as documented in tango.time.Time 886 bool DTyear(int year) { return year >= -10000 && year <= 9999; } 887 888 void addMonths(ref FullDate d, int n) { d.val = Gregorian.generic.addMonths(d.val, n-1); } // -1 due to initial being 1 889 void addDays (ref FullDate d, int n) { d.val += TimeSpan.fromDays (n-1); } // ditto 890 void addHours (ref FullDate d, int n) { d.val += TimeSpan.fromHours (n); } 891 void addMins (ref FullDate d, int n) { d.val += TimeSpan.fromMinutes(n); } 892 void addSecs (ref FullDate d, int n) { d.val += TimeSpan.fromSeconds(n); } 893 void addMs (ref FullDate d, int n) { d.val += TimeSpan.fromMillis (n); } 894 895 // years and secs always just get the DT value 896 int years (const(FullDate) d) { return Gregorian.generic.getYear (d.val); } 897 int months(const(FullDate) d) { return Gregorian.generic.getMonth (d.val); } 898 int days (const(FullDate) d) { return Gregorian.generic.getDayOfMonth(d.val); } 899 int hours (const(FullDate) d) { return d.val.time.hours; } 900 int mins (const(FullDate) d) { return d.val.time.minutes; } 901 int secs (const(FullDate) d) { return d.val.time.seconds; } 902 int ms (const(FullDate) d) { return d.val.time.millis; } 903 904 //////////////////// 905 906 // Unit tests 907 908 debug (UnitTest) { 909 // void main() {} 910 911 debug (Tango_ISO8601_Valgrind) import tango.stdc.stdlib : malloc, free; 912 913 unittest { 914 FullDate fd; 915 916 // date 917 918 size_t d(const(char[]) s, ubyte e = 0) { 919 fd = fd.init; 920 return parseDate(s, fd, e); 921 } 922 923 auto 924 INIT_YEAR = years (FullDate.init), 925 INIT_MONTH = months(FullDate.init), 926 INIT_DAY = days (FullDate.init); 927 928 assert (d("20abc") == 2); 929 assert (years(fd) == 2000); 930 931 assert (d("2004") == 4); 932 assert (years(fd) == 2004); 933 934 assert (d("+0019", 2) == 5); 935 assert (years(fd) == 1900); 936 937 assert (d("+111985", 2) == 7); 938 assert (years(fd) == INIT_YEAR); 939 assert (fd.year == 111985); 940 941 assert (d("+111985", 1) == 6); 942 assert (years(fd) == INIT_YEAR); 943 assert (fd.year == 11198); 944 945 assert (d("+111985", 3) == 0); 946 assert (years(fd) == INIT_YEAR); 947 assert (fd.year == INIT_YEAR); 948 949 assert (d("+111985", 4) == 7); 950 assert (years(fd) == INIT_YEAR); 951 assert (fd.year == 11198500); 952 953 assert (d("-111985", 5) == 0); 954 assert (years(fd) == INIT_YEAR); 955 assert (fd.year == INIT_YEAR); 956 957 assert (d("+999999999", 5) == 10); 958 assert (years(fd) == INIT_YEAR); 959 assert (fd.year == 999_999_999); 960 961 try { 962 d("+10000000000", 6); 963 assert (false); 964 } catch (IllegalArgumentException) { 965 assert (years(fd) == INIT_YEAR); 966 assert (fd.year == INIT_YEAR); 967 } 968 969 assert (d("-999999999", 5) == 10); 970 assert (years(fd) == INIT_YEAR); 971 assert (fd.year == -1_000_000_000); 972 973 assert (d("0001") == 4); 974 assert (years(fd) == 1); 975 assert (fd.year == 1); 976 977 assert (d("0000") == 4); 978 assert (fd.year == -1); 979 980 assert (d("-0001") == 5); 981 assert (fd.year == -2); 982 983 assert (d("abc") == 0); 984 assert (years(fd) == INIT_YEAR); 985 assert (fd.year == INIT_YEAR); 986 987 assert (d("abc123") == 0); 988 assert (years(fd) == INIT_YEAR); 989 assert (fd.year == INIT_YEAR); 990 991 assert (d("2007-08") == 7); 992 assert (years(fd) == 2007); 993 assert (months(fd) == 8); 994 995 assert (d("+001985-04", 2) == 10); 996 assert (years(fd) == 1985); 997 assert (months(fd) == 4); 998 999 assert (d("2007-08-07") == 10); 1000 assert (years(fd) == 2007); 1001 assert (months(fd) == 8); 1002 assert (days(fd) == 7); 1003 1004 assert (d("2008-20-30") == 4); 1005 assert (years(fd) == 2008); 1006 assert (months(fd) == INIT_MONTH); 1007 1008 assert (d("2007-02-30") == 7); 1009 assert (years(fd) == 2007); 1010 assert (months(fd) == 2); 1011 1012 assert (d("20060708") == 8); 1013 assert (years(fd) == 2006); 1014 assert (months(fd) == 7); 1015 assert (days(fd) == 8); 1016 1017 assert (d("19953080") == 4); 1018 assert (years(fd) == 1995); 1019 assert (months(fd) == INIT_MONTH); 1020 1021 assert (d("2007-0201") == 7); 1022 assert (years(fd) == 2007); 1023 assert (months(fd) == 2); 1024 1025 assert (d("200702-01") == 6); 1026 assert (years(fd) == 2007); 1027 assert (months(fd) == 2); 1028 1029 assert (d("+001985-04-12", 2) == 13); 1030 assert (years(fd) == 1985); 1031 assert (fd.year == 1985); 1032 assert (months(fd) == 4); 1033 assert (days(fd) == 12); 1034 1035 assert (d("-0123450607", 2) == 11); 1036 assert (years(fd) == INIT_YEAR); 1037 assert (fd.year == -12346); 1038 assert (months(fd) == 6); 1039 assert (days(fd) == 7); 1040 1041 assert (d("1985W15") == 7); 1042 assert (years(fd) == 1985); 1043 assert (months(fd) == 4); 1044 assert (days(fd) == 8); 1045 1046 assert (d("2008-W01") == 8); 1047 assert (years(fd) == 2007); 1048 assert (months(fd) == 12); 1049 assert (days(fd) == 31); 1050 1051 assert (d("2008-W012") == 8); 1052 assert (years(fd) == 2007); 1053 assert (months(fd) == 12); 1054 assert (days(fd) == 31); 1055 1056 assert (d("2008W01-2") == 7); 1057 assert (years(fd) == 2007); 1058 assert (months(fd) == 12); 1059 assert (days(fd) == 31); 1060 1061 assert (d("2008-W01-2") == 10); 1062 assert (years(fd) == 2008); 1063 assert (months(fd) == 1); 1064 assert (days(fd) == 1); 1065 1066 assert (d("2009-W53-4") == 10); 1067 assert (years(fd) == 2009); 1068 assert (months(fd) == 12); 1069 assert (days(fd) == 31); 1070 1071 assert (d("2009-W01-1") == 10); 1072 assert (years(fd) == 2008); 1073 assert (months(fd) == 12); 1074 assert (days(fd) == 29); 1075 1076 assert (d("2009W537") == 8); 1077 assert (years(fd) == 2010); 1078 assert (months(fd) == 1); 1079 assert (days(fd) == 3); 1080 1081 assert (d("2010W537") == 4); 1082 assert (years(fd) == 2010); 1083 assert (months(fd) == INIT_MONTH); 1084 1085 assert (d("2009-W01-3") == 10); 1086 assert (years(fd) == 2008); 1087 assert (months(fd) == 12); 1088 assert (days(fd) == 31); 1089 1090 assert (d("2009-W01-4") == 10); 1091 assert (years(fd) == 2009); 1092 assert (months(fd) == 1); 1093 assert (days(fd) == 1); 1094 1095 assert (d("2004-W53-6") == 10); 1096 assert (years(fd) == 2005); 1097 assert (months(fd) == 1); 1098 assert (days(fd) == 1); 1099 1100 assert (d("2004-W53-7") == 10); 1101 assert (years(fd) == 2005); 1102 assert (months(fd) == 1); 1103 assert (days(fd) == 2); 1104 1105 assert (d("2005-W52-6") == 10); 1106 assert (years(fd) == 2005); 1107 assert (months(fd) == 12); 1108 assert (days(fd) == 31); 1109 1110 assert (d("2007-W01-1") == 10); 1111 assert (years(fd) == 2007); 1112 assert (months(fd) == 1); 1113 assert (days(fd) == 1); 1114 1115 assert (d("1000-W07-7") == 10); 1116 assert (years(fd) == 1000); 1117 assert (months(fd) == 2); 1118 assert (days(fd) == 16); 1119 1120 assert (d("1500-W11-1") == 10); 1121 assert (years(fd) == 1500); 1122 assert (months(fd) == 3); 1123 assert (days(fd) == 12); 1124 1125 assert (d("1700-W14-2") == 10); 1126 assert (years(fd) == 1700); 1127 assert (months(fd) == 4); 1128 assert (days(fd) == 6); 1129 1130 assert (d("1800-W19-3") == 10); 1131 assert (years(fd) == 1800); 1132 assert (months(fd) == 5); 1133 assert (days(fd) == 7); 1134 1135 assert (d("1900-W25-4") == 10); 1136 assert (years(fd) == 1900); 1137 assert (months(fd) == 6); 1138 assert (days(fd) == 21); 1139 1140 assert (d("0900-W27-5") == 10); 1141 assert (years(fd) == 900); 1142 assert (months(fd) == 7); 1143 assert (days(fd) == 9); 1144 1145 assert (d("0800-W33-6") == 10); 1146 assert (years(fd) == 800); 1147 assert (months(fd) == 8); 1148 assert (days(fd) == 19); 1149 1150 assert (d("0700-W37-7") == 10); 1151 assert (years(fd) == 700); 1152 assert (months(fd) == 9); 1153 assert (days(fd) == 16); 1154 1155 assert (d("0600-W41-4") == 10); 1156 assert (years(fd) == 600); 1157 assert (months(fd) == 10); 1158 assert (days(fd) == 9); 1159 1160 assert (d("0500-W45-7") == 10); 1161 assert (years(fd) == 500); 1162 assert (months(fd) == 11); 1163 assert (days(fd) == 14); 1164 1165 assert (d("2000-W55") == 4); 1166 assert (years(fd) == 2000); 1167 1168 assert (d("1980-002") == 8); 1169 assert (years(fd) == 1980); 1170 assert (months(fd) == 1); 1171 assert (days(fd) == 2); 1172 1173 assert (d("1981-034") == 8); 1174 assert (years(fd) == 1981); 1175 assert (months(fd) == 2); 1176 assert (days(fd) == 3); 1177 1178 assert (d("1982-063") == 8); 1179 assert (years(fd) == 1982); 1180 assert (months(fd) == 3); 1181 assert (days(fd) == 4); 1182 1183 assert (d("1983-095") == 8); 1184 assert (years(fd) == 1983); 1185 assert (months(fd) == 4); 1186 assert (days(fd) == 5); 1187 1188 assert (d("1984-127") == 8); 1189 assert (years(fd) == 1984); 1190 assert (months(fd) == 5); 1191 assert (days(fd) == 6); 1192 1193 assert (d("1985-158") == 8); 1194 assert (years(fd) == 1985); 1195 assert (months(fd) == 6); 1196 assert (days(fd) == 7); 1197 1198 assert (d("1986-189") == 8); 1199 assert (years(fd) == 1986); 1200 assert (months(fd) == 7); 1201 assert (days(fd) == 8); 1202 1203 assert (d("1987-221") == 8); 1204 assert (years(fd) == 1987); 1205 assert (months(fd) == 8); 1206 assert (days(fd) == 9); 1207 1208 assert (d("1988-254") == 8); 1209 assert (years(fd) == 1988); 1210 assert (months(fd) == 9); 1211 assert (days(fd) == 10); 1212 1213 assert (d("1989-284") == 8); 1214 assert (years(fd) == 1989); 1215 assert (months(fd) == 10); 1216 assert (days(fd) == 11); 1217 1218 assert (d("1990316") == 7); 1219 assert (years(fd) == 1990); 1220 assert (months(fd) == 11); 1221 assert (days(fd) == 12); 1222 1223 assert (d("1991-347") == 8); 1224 assert (years(fd) == 1991); 1225 assert (months(fd) == 12); 1226 assert (days(fd) == 13); 1227 1228 assert (d("1992-000") == 4); 1229 assert (years(fd) == 1992); 1230 1231 assert (d("1993-370") == 4); 1232 assert (years(fd) == 1993); 1233 1234 // time 1235 1236 size_t t(const(char[]) s) { 1237 fd = fd.init; 1238 return parseTime(s, fd); 1239 } 1240 1241 assert (t("20") == 2); 1242 assert (hours(fd) == 20); 1243 assert (mins(fd) == 0); 1244 assert (secs(fd) == 0); 1245 1246 assert (t("30") == 0); 1247 1248 assert (t("T15") == 3); 1249 assert (hours(fd) == 15); 1250 assert (mins(fd) == 0); 1251 assert (secs(fd) == 0); 1252 1253 assert (t("T1") == 0); 1254 assert (t("T") == 0); 1255 1256 assert (t("2004") == 4); 1257 assert (hours(fd) == 20); 1258 assert (mins(fd) == 4); 1259 assert (secs(fd) == 0); 1260 1261 assert (t("200406") == 6); 1262 assert (hours(fd) == 20); 1263 assert (mins(fd) == 4); 1264 assert (secs(fd) == 6); 1265 1266 assert (t("24:00") == 5); 1267 assert (fd.endOfDay); 1268 assert (days(fd) == INIT_DAY + 1); 1269 assert (hours(fd) == 0); 1270 assert (mins(fd) == 0); 1271 assert (secs(fd) == 0); 1272 1273 assert (t("00:00") == 5); 1274 assert (hours(fd) == 0); 1275 assert (mins(fd) == 0); 1276 assert (secs(fd) == 0); 1277 1278 assert (t("23:59:60") == 8); 1279 assert (hours(fd) == 23); 1280 assert (mins(fd) == 59); 1281 assert (secs(fd) == 59); 1282 assert (fd.seconds == 60); 1283 1284 assert (t("12:3456") == 5); 1285 assert (hours(fd) == 12); 1286 assert (mins(fd) == 34); 1287 1288 assert (t("1234:56") == 4); 1289 assert (hours(fd) == 12); 1290 assert (mins(fd) == 34); 1291 1292 assert (t("16:49:30,001") == 12); 1293 assert (hours(fd) == 16); 1294 assert (mins(fd) == 49); 1295 assert (secs(fd) == 30); 1296 assert (ms(fd) == 1); 1297 1298 assert (t("15:48:29,1") == 10); 1299 assert (hours(fd) == 15); 1300 assert (mins(fd) == 48); 1301 assert (secs(fd) == 29); 1302 assert (ms(fd) == 100); 1303 1304 assert (t("02:10:34,a") == 8); 1305 assert (hours(fd) == 2); 1306 assert (mins(fd) == 10); 1307 assert (secs(fd) == 34); 1308 1309 assert (t("14:50,5") == 7); 1310 assert (hours(fd) == 14); 1311 assert (mins(fd) == 50); 1312 assert (secs(fd) == 30); 1313 1314 assert (t("1540,4") == 6); 1315 assert (hours(fd) == 15); 1316 assert (mins(fd) == 40); 1317 assert (secs(fd) == 24); 1318 1319 assert (t("1250,") == 4); 1320 assert (hours(fd) == 12); 1321 assert (mins(fd) == 50); 1322 1323 assert (t("14,5") == 4); 1324 assert (hours(fd) == 14); 1325 assert (mins(fd) == 30); 1326 1327 assert (t("12,") == 2); 1328 assert (hours(fd) == 12); 1329 assert (mins(fd) == 0); 1330 1331 assert (t("24:00:01") == 5); 1332 assert (fd.endOfDay); 1333 assert (hours(fd) == 0); 1334 assert (mins(fd) == 0); 1335 assert (secs(fd) == 0); 1336 1337 assert (t("12:34+:56") == 5); 1338 assert (hours(fd) == 12); 1339 assert (mins(fd) == 34); 1340 assert (secs(fd) == 0); 1341 1342 // time zones 1343 1344 assert (t("14:45:15Z") == 9); 1345 assert (hours(fd) == 14); 1346 assert (mins(fd) == 45); 1347 assert (secs(fd) == 15); 1348 1349 assert (t("23Z") == 3); 1350 assert (hours(fd) == 23); 1351 assert (mins(fd) == 0); 1352 assert (secs(fd) == 0); 1353 1354 assert (t("21:32:43-12:34") == 14); 1355 assert (days(fd) == INIT_DAY + 1); 1356 assert (hours(fd) == 10); 1357 assert (mins(fd) == 6); 1358 assert (secs(fd) == 43); 1359 1360 assert (t("12:34,5+00:00") == 13); 1361 assert (hours(fd) == 12); 1362 assert (mins(fd) == 34); 1363 assert (secs(fd) == 30); 1364 1365 assert (t("03:04+07") == 8); 1366 assert (hours(fd) == 20); 1367 assert (mins(fd) == 4); 1368 assert (secs(fd) == 0); 1369 1370 assert (t("11,5+") == 4); 1371 assert (hours(fd) == 11); 1372 assert (mins(fd) == 30); 1373 1374 assert (t("07-") == 2); 1375 assert (hours(fd) == 7); 1376 1377 assert (t("06:12,7-") == 7); 1378 assert (hours(fd) == 6); 1379 assert (mins(fd) == 12); 1380 assert (secs(fd) == 42); 1381 1382 assert (t("050403,2+") == 8); 1383 assert (hours(fd) == 5); 1384 assert (mins(fd) == 4); 1385 assert (secs(fd) == 3); 1386 assert (ms(fd) == 200); 1387 1388 assert (t("061656-") == 6); 1389 assert (hours(fd) == 6); 1390 assert (mins(fd) == 16); 1391 assert (secs(fd) == 56); 1392 1393 // date and time together 1394 1395 size_t b(const(char[]) s) { 1396 fd = fd.init; 1397 return parseDateAndTime(s, fd); 1398 } 1399 1400 assert (b("2007-08-09T12:34:56") == 19); 1401 assert (years(fd) == 2007); 1402 assert (months(fd) == 8); 1403 assert (days(fd) == 9); 1404 assert (hours(fd) == 12); 1405 assert (mins(fd) == 34); 1406 assert (secs(fd) == 56); 1407 1408 assert (b("1985W155T235030,768") == 19); 1409 assert (years(fd) == 1985); 1410 assert (months(fd) == 4); 1411 assert (days(fd) == 12); 1412 assert (hours(fd) == 23); 1413 assert (mins(fd) == 50); 1414 assert (secs(fd) == 30); 1415 assert (ms(fd) == 768); 1416 1417 // time zones 1418 1419 assert (b("2009-08-07T01:02:03Z") == 20); 1420 assert (years(fd) == 2009); 1421 assert (months(fd) == 8); 1422 assert (days(fd) == 7); 1423 assert (hours(fd) == 1); 1424 assert (mins(fd) == 2); 1425 assert (secs(fd) == 3); 1426 1427 assert (b("2007-08-09T03:02,5+04:56") == 24); 1428 assert (years(fd) == 2007); 1429 assert (months(fd) == 8); 1430 assert (days(fd) == 8); 1431 assert (hours(fd) == 22); 1432 assert (mins(fd) == 6); 1433 assert (secs(fd) == 30); 1434 1435 assert (b("20000228T2330-01") == 16); 1436 assert (years(fd) == 2000); 1437 assert (months(fd) == 2); 1438 assert (days(fd) == 29); 1439 assert (hours(fd) == 0); 1440 assert (mins(fd) == 30); 1441 assert (secs(fd) == 0); 1442 1443 assert (b("2007-01-01T00:00+01") == 19); 1444 assert (years(fd) == 2006); 1445 assert (months(fd) == 12); 1446 assert (days(fd) == 31); 1447 assert (hours(fd) == 23); 1448 assert (mins(fd) == 0); 1449 assert (secs(fd) == 0); 1450 1451 assert (b("2007-12-31T23:00-01") == 19); 1452 assert (fd.endOfDay); 1453 assert (years(fd) == 2008); 1454 assert (months(fd) == 1); 1455 assert (days(fd) == 1); 1456 assert (hours(fd) == 0); 1457 assert (mins(fd) == 0); 1458 assert (secs(fd) == 0); 1459 1460 assert (b("2007-12-31T23:01-01") == 19); 1461 assert (!fd.endOfDay); 1462 assert (years(fd) == 2008); 1463 assert (months(fd) == 1); 1464 assert (days(fd) == 1); 1465 assert (hours(fd) == 0); 1466 assert (mins(fd) == 1); 1467 assert (secs(fd) == 0); 1468 1469 assert (b("1902-03-04T1a") == 0); 1470 assert (b("1902-03-04T10:aa") == 0); 1471 assert (b("1902-03-04T10:1aa") == 0); 1472 assert (b("200512-01T10:02") == 0); 1473 assert (b("1985-04-1210:15:30+04:00") == 0); 1474 assert (b("1985-04-12T10:15:30+0400") == 0); 1475 assert (b("19020304T05:06:07") == 0); 1476 assert (b("1902-03-04T050607") == 0); 1477 assert (b("19020304T05:06:07abcd") == 0); 1478 assert (b("1902-03-04T050607abcd") == 0); 1479 1480 assert (b("1985-04-12T10:15:30-05:4") == 22); 1481 assert (years(fd) == 1985); 1482 assert (months(fd) == 4); 1483 assert (days(fd) == 12); 1484 assert (hours(fd) == 15); 1485 assert (mins(fd) == 15); 1486 assert (secs(fd) == 30); 1487 assert (b("2009-04-13T23:00-01") == 19); 1488 assert (fd.endOfDay); 1489 assert (years(fd) == 2009); 1490 assert (months(fd) == 4); 1491 assert (days(fd) == 14); 1492 assert (hours(fd) == 0); 1493 assert (mins(fd) == 0); 1494 assert (secs(fd) == 0); 1495 assert (b("2009-04-13T24:00Z") == 17); 1496 assert (fd.endOfDay); 1497 assert (years(fd) == 2009); 1498 assert (months(fd) == 4); 1499 assert (days(fd) == 14); 1500 assert (hours(fd) == 0); 1501 assert (mins(fd) == 0); 1502 assert (secs(fd) == 0); 1503 1504 // unimplemented: intervals, durations, recurring intervals 1505 1506 debug (Tango_ISO8601_Valgrind) { 1507 size_t valgrind(size_t delegate(const(char[])) f, const(char[]) s) { 1508 auto p = cast(char*)malloc(s.length); 1509 auto ps = p[0..s.length]; 1510 ps[] = s[]; 1511 auto result = f(ps); 1512 free(p); 1513 return result; 1514 } 1515 size_t vd(const(char[]) s) { 1516 size_t date(const(char[]) ss) { return d(ss); } 1517 return valgrind(&date, s); 1518 } 1519 size_t vt(const(char[]) s) { return valgrind(&t, s); } 1520 size_t vb(const(char[]) s) { return valgrind(&b, s); } 1521 1522 assert (vd("1") == 0); 1523 assert (vd("19") == 2); 1524 assert (vd("199") == 0); 1525 assert (vd("1999") == 4); 1526 assert (vd("1999-") == 4); 1527 assert (vd("1999-W") == 4); 1528 assert (vd("1999-W0") == 4); 1529 assert (vd("1999-W01") == 8); 1530 assert (vd("1999-W01-") == 8); 1531 assert (vd("1999-W01-3") == 10); 1532 assert (vd("1999W") == 4); 1533 assert (vd("1999W0") == 4); 1534 assert (vd("1999W01") == 7); 1535 assert (vd("1999W01-") == 7); 1536 assert (vd("1999W01-3") == 7); 1537 assert (vd("1999W013") == 8); 1538 assert (vd("1999-0") == 4); 1539 assert (vd("1999-01") == 7); 1540 assert (vd("1999-01-") == 7); 1541 assert (vd("1999-01-0") == 7); 1542 assert (vd("1999-01-01") == 10); 1543 assert (vd("1999-0101") == 7); 1544 assert (vd("1999-365") == 8); 1545 assert (vd("1999365") == 7); 1546 1547 assert (vt("1") == 0); 1548 assert (vt("15") == 2); 1549 assert (vt("15:") == 2); 1550 assert (vt("15:3") == 2); 1551 assert (vt("15:30") == 5); 1552 assert (vt("153") == 2); 1553 assert (vt("1530") == 4); 1554 assert (vt("1530:") == 4); 1555 assert (vt("15304") == 4); 1556 assert (vt("153045") == 6); 1557 assert (vt("15:30:") == 5); 1558 assert (vt("15:30:4") == 5); 1559 assert (vt("15:30:45") == 8); 1560 assert (vt("T15") == 3); 1561 assert (vt("T1") == 0); 1562 assert (vt("T") == 0); 1563 assert (vt("15,") == 2); 1564 assert (vt("15,2") == 4); 1565 assert (vt("1530,") == 4); 1566 assert (vt("1530,2") == 6); 1567 assert (vt("15:30:45,") == 8); 1568 assert (vt("15:30:45,2") == 10); 1569 assert (vt("153045,") == 6); 1570 assert (vt("153045,2") == 8); 1571 assert (vt("153045,22") == 9); 1572 assert (vt("153045,222") == 10); 1573 assert (vt("15Z") == 3); 1574 assert (vt("15+") == 2); 1575 assert (vt("15-") == 2); 1576 assert (vt("15+0") == 2); 1577 assert (vt("15+00") == 5); 1578 assert (vt("15+00:") == 5); 1579 assert (vt("15+00:0") == 5); 1580 assert (vt("15+00:00") == 8); 1581 assert (vb("1999-01-01") == 0); 1582 assert (vb("1999-01-01T") == 0); 1583 assert (vb("1999-01-01T15:30:45") == 19); 1584 } 1585 } 1586 }