1 // Written in the D programming language. 2 3 /** 4 * $(RED Deprecated. It will be removed in February 2012. 5 * Please use std.datetime instead.) 6 * 7 * Dates are represented in several formats. The date implementation 8 * revolves around a central type, $(D d_time), from which other 9 * formats are converted to and from. Dates are calculated using the 10 * Gregorian calendar. 11 * 12 * References: $(WEB wikipedia.org/wiki/Gregorian_calendar, Gregorian 13 * calendar (Wikipedia)) 14 * 15 * Macros: WIKI = Phobos/StdDate 16 * 17 * Copyright: Copyright Digital Mars 2000 - 2009. 18 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 19 * Authors: $(WEB digitalmars.com, Walter Bright) 20 * Source: $(PHOBOSSRC std/_date.d) 21 */ 22 /* Copyright Digital Mars 2000 - 2009. 23 * Distributed under the Boost Software License, Version 1.0. 24 * (See accompanying file LICENSE_1_0.txt or copy at 25 * http://www.boost.org/LICENSE_1_0.txt) 26 */ 27 module undead.date; 28 29 import std.conv, std.exception, std.stdio; 30 import core.stdc.stdlib; 31 32 import undead.datebase; 33 import undead.dateparse; 34 35 /+ 36 pragma(msg, "Notice: As of Phobos 2.055, std.date and std.dateparse have been " ~ 37 "deprecated. They will be removed in February 2012. " ~ 38 "Please use std.datetime instead."); 39 40 deprecated: 41 +/ 42 43 /** 44 * $(D d_time) is a signed arithmetic type giving the time elapsed 45 * since January 1, 1970. Negative values are for dates preceding 46 * 1970. The time unit used is Ticks. Ticks are milliseconds or 47 * smaller intervals. 48 * 49 * The usual arithmetic operations can be performed on d_time, such as adding, 50 * subtracting, etc. Elapsed time in Ticks can be computed by subtracting a 51 * starting d_time from an ending d_time. 52 */ 53 alias long d_time; 54 55 /** 56 * A value for d_time that does not represent a valid time. 57 */ 58 enum d_time d_time_nan = long.min; 59 60 /** 61 * Time broken down into its components. 62 */ 63 struct Date 64 { 65 int year = int.min; /// use int.min as "nan" year value 66 int month; /// 1..12 67 int day; /// 1..31 68 int hour; /// 0..23 69 int minute; /// 0..59 70 int second; /// 0..59 71 int ms; /// 0..999 72 int weekday; /// 0: not specified, 1..7: Sunday..Saturday 73 int tzcorrection = int.min; /// -1200..1200 correction in hours 74 75 /// Parse date out of string s[] and store it in this Date instance. 76 void parse(string s) 77 { 78 DateParse dp; 79 dp.parse(s, this); 80 } 81 } 82 83 enum 84 { 85 hoursPerDay = 24, 86 minutesPerHour = 60, 87 msPerMinute = 60 * 1000, 88 msPerHour = 60 * msPerMinute, 89 msPerDay = 86_400_000, 90 ticksPerMs = 1, 91 ticksPerSecond = 1000, /// Will be at least 1000 92 ticksPerMinute = ticksPerSecond * 60, 93 ticksPerHour = ticksPerMinute * 60, 94 ticksPerDay = ticksPerHour * 24, 95 } 96 97 deprecated alias ticksPerSecond TicksPerSecond; 98 deprecated alias ticksPerMs TicksPerMs; 99 deprecated alias ticksPerMinute TicksPerMinute; 100 deprecated alias ticksPerHour TicksPerHour; 101 deprecated alias ticksPerDay TicksPerDay; 102 103 deprecated 104 unittest 105 { 106 assert(ticksPerSecond == TicksPerSecond); 107 } 108 109 __gshared d_time localTZA = 0; 110 111 private immutable char[] daystr = "SunMonTueWedThuFriSat"; 112 private immutable char[] monstr = "JanFebMarAprMayJunJulAugSepOctNovDec"; 113 114 private immutable int[12] mdays = 115 [ 0,31,59,90,120,151,181,212,243,273,304,334 ]; 116 117 /******************************** 118 * Compute year and week [1..53] from t. The ISO 8601 week 1 is the first week 119 * of the year that includes January 4. Monday is the first day of the week. 120 * References: 121 * $(LINK2 http://en.wikipedia.org/wiki/ISO_8601, ISO 8601 (Wikipedia)) 122 */ 123 124 void toISO8601YearWeek(d_time t, out int year, out int week) 125 { 126 year = yearFromTime(t); 127 128 auto yday = day(t) - dayFromYear(year); 129 130 /* Determine day of week Jan 4 falls on. 131 * Weeks begin on a Monday. 132 */ 133 134 auto d = dayFromYear(year); 135 auto w = (d + 3/*Jan4*/ + 3) % 7; 136 if (w < 0) 137 w += 7; 138 139 /* Find yday of beginning of ISO 8601 year 140 */ 141 auto ydaybeg = 3/*Jan4*/ - w; 142 143 /* Check if yday is actually the last week of the previous year 144 */ 145 if (yday < ydaybeg) 146 { 147 year -= 1; 148 week = 53; 149 return; 150 } 151 152 /* Check if yday is actually the first week of the next year 153 */ 154 if (yday >= 362) // possible 155 { int d2; 156 int ydaybeg2; 157 158 d2 = dayFromYear(year + 1); 159 w = (d2 + 3/*Jan4*/ + 3) % 7; 160 if (w < 0) 161 w += 7; 162 //printf("w = %d\n", w); 163 ydaybeg2 = 3/*Jan4*/ - w; 164 if (d + yday >= d2 + ydaybeg2) 165 { 166 year += 1; 167 week = 1; 168 return; 169 } 170 } 171 172 week = (yday - ydaybeg) / 7 + 1; 173 } 174 175 /* *********************************** 176 * Divide time by divisor. Always round down, even if d is negative. 177 */ 178 179 pure d_time floor(d_time d, int divisor) 180 { 181 return (d < 0 ? d - divisor - 1 : d) / divisor; 182 } 183 184 int dmod(d_time n, d_time d) 185 { d_time r; 186 187 r = n % d; 188 if (r < 0) 189 r += d; 190 assert(cast(int)r == r); 191 return cast(int)r; 192 } 193 194 /******************************** 195 * Calculates the hour from time. 196 * 197 * Params: 198 * time = The time to compute the hour from. 199 * Returns: 200 * The calculated hour, 0..23. 201 */ 202 int hourFromTime(d_time time) 203 { 204 return dmod(floor(time, msPerHour), hoursPerDay); 205 } 206 207 /******************************** 208 * Calculates the minute from time. 209 * 210 * Params: 211 * time = The time to compute the minute from. 212 * Returns: 213 * The calculated minute, 0..59. 214 */ 215 int minFromTime(d_time time) 216 { 217 return dmod(floor(time, msPerMinute), minutesPerHour); 218 } 219 220 /******************************** 221 * Calculates the second from time. 222 * 223 * Params: 224 * time = The time to compute the second from. 225 * Returns: 226 * The calculated second, 0..59. 227 */ 228 int secFromTime(d_time time) 229 { 230 return dmod(floor(time, ticksPerSecond), 60); 231 } 232 233 /******************************** 234 * Calculates the milisecond from time. 235 * 236 * Params: 237 * time = The time to compute the milisecond from. 238 * Returns: 239 * The calculated milisecond, 0..999. 240 */ 241 int msFromTime(d_time time) 242 { 243 return dmod(time / (ticksPerSecond / 1000), 1000); 244 } 245 246 int timeWithinDay(d_time t) 247 { 248 return dmod(t, msPerDay); 249 } 250 251 d_time toInteger(d_time n) 252 { 253 return n; 254 } 255 256 int day(d_time t) 257 { 258 return cast(int)floor(t, msPerDay); 259 } 260 261 pure bool leapYear(uint y) 262 { 263 return (y % 4) == 0 && (y % 100 || (y % 400) == 0); 264 } 265 266 unittest { 267 assert(!leapYear(1970)); 268 assert(leapYear(1984)); 269 assert(leapYear(2000)); 270 assert(!leapYear(2100)); 271 } 272 273 /******************************** 274 * Calculates the number of days that exists in a year. 275 * 276 * Leap years have 366 days, while other years have 365. 277 * 278 * Params: 279 * year = The year to compute the number of days from. 280 * Returns: 281 * The number of days in the year, 365 or 366. 282 */ 283 pure uint daysInYear(uint year) 284 { 285 return (leapYear(year) ? 366 : 365); 286 } 287 288 289 /******************************** 290 * Calculates the number of days elapsed since 1 January 1970 291 * until 1 January of the given year. 292 * 293 * Params: 294 * year = The year to compute the number of days from. 295 * Returns: 296 * The number of days elapsed. 297 * 298 * Example: 299 * ---------- 300 * writeln(dayFromYear(1970)); // writes '0' 301 * writeln(dayFromYear(1971)); // writes '365' 302 * writeln(dayFromYear(1972)); // writes '730' 303 * ---------- 304 */ 305 pure int dayFromYear(int year) 306 { 307 return cast(int) (365 * (year - 1970) + 308 floor((year - 1969), 4) - 309 floor((year - 1901), 100) + 310 floor((year - 1601), 400)); 311 } 312 313 pure d_time timeFromYear(int y) 314 { 315 return cast(d_time)msPerDay * dayFromYear(y); 316 } 317 318 /***************************** 319 * Calculates the year from the d_time t. 320 */ 321 322 pure int yearFromTime(d_time t) 323 { 324 325 if (t == d_time_nan) 326 return 0; 327 328 // Hazard a guess 329 //y = 1970 + cast(int) (t / (365.2425 * msPerDay)); 330 // Use integer only math 331 int y = 1970 + cast(int) (t / (3652425 * (msPerDay / 10000))); 332 333 if (timeFromYear(y) <= t) 334 { 335 while (timeFromYear(y + 1) <= t) 336 y++; 337 } 338 else 339 { 340 do 341 { 342 y--; 343 } 344 while (timeFromYear(y) > t); 345 } 346 return y; 347 } 348 349 /******************************* 350 * Determines if d_time t is a leap year. 351 * 352 * A leap year is every 4 years except years ending in 00 that are not 353 * divsible by 400. 354 * 355 * Returns: !=0 if it is a leap year. 356 * 357 * References: 358 * $(LINK2 http://en.wikipedia.org/wiki/Leap_year, Wikipedia) 359 */ 360 361 pure bool inLeapYear(d_time t) 362 { 363 return leapYear(yearFromTime(t)); 364 } 365 366 /***************************** 367 * Calculates the month from the d_time t. 368 * 369 * Returns: Integer in the range 0..11, where 370 * 0 represents January and 11 represents December. 371 */ 372 373 int monthFromTime(d_time t) 374 { 375 auto year = yearFromTime(t); 376 auto day = day(t) - dayFromYear(year); 377 378 int month; 379 if (day < 59) 380 { 381 if (day < 31) 382 { assert(day >= 0); 383 month = 0; 384 } 385 else 386 month = 1; 387 } 388 else 389 { 390 day -= leapYear(year); 391 if (day < 212) 392 { 393 if (day < 59) 394 month = 1; 395 else if (day < 90) 396 month = 2; 397 else if (day < 120) 398 month = 3; 399 else if (day < 151) 400 month = 4; 401 else if (day < 181) 402 month = 5; 403 else 404 month = 6; 405 } 406 else 407 { 408 if (day < 243) 409 month = 7; 410 else if (day < 273) 411 month = 8; 412 else if (day < 304) 413 month = 9; 414 else if (day < 334) 415 month = 10; 416 else if (day < 365) 417 month = 11; 418 else 419 assert(0); 420 } 421 } 422 return month; 423 } 424 425 /******************************* 426 * Compute which day in a month a d_time t is. 427 * Returns: 428 * Integer in the range 1..31 429 */ 430 int dateFromTime(d_time t) 431 { 432 auto year = yearFromTime(t); 433 auto day = day(t) - dayFromYear(year); 434 auto leap = leapYear(year); 435 auto month = monthFromTime(t); 436 int date; 437 switch (month) 438 { 439 case 0: date = day + 1; break; 440 case 1: date = day - 30; break; 441 case 2: date = day - 58 - leap; break; 442 case 3: date = day - 89 - leap; break; 443 case 4: date = day - 119 - leap; break; 444 case 5: date = day - 150 - leap; break; 445 case 6: date = day - 180 - leap; break; 446 case 7: date = day - 211 - leap; break; 447 case 8: date = day - 242 - leap; break; 448 case 9: date = day - 272 - leap; break; 449 case 10: date = day - 303 - leap; break; 450 case 11: date = day - 333 - leap; break; 451 default: 452 assert(0); 453 } 454 return date; 455 } 456 457 /******************************* 458 * Compute which day of the week a d_time t is. 459 * Returns: 460 * Integer in the range 0..6, where 0 represents Sunday 461 * and 6 represents Saturday. 462 */ 463 int weekDay(d_time t) 464 { 465 auto w = (cast(int)day(t) + 4) % 7; 466 if (w < 0) 467 w += 7; 468 return w; 469 } 470 471 /*********************************** 472 * Convert from UTC to local time. 473 */ 474 475 d_time UTCtoLocalTime(d_time t) 476 { 477 return (t == d_time_nan) 478 ? d_time_nan 479 : t + localTZA + daylightSavingTA(t); 480 } 481 482 /*********************************** 483 * Convert from local time to UTC. 484 */ 485 486 d_time localTimetoUTC(d_time t) 487 { 488 return (t == d_time_nan) 489 ? d_time_nan 490 /* BUGZILLA 1752 says this line should be: 491 * : t - localTZA - daylightSavingTA(t); 492 */ 493 : t - localTZA - daylightSavingTA(t - localTZA); 494 } 495 496 497 d_time makeTime(d_time hour, d_time min, d_time sec, d_time ms) 498 { 499 return hour * ticksPerHour + 500 min * ticksPerMinute + 501 sec * ticksPerSecond + 502 ms * ticksPerMs; 503 } 504 505 /* ***************************** 506 * Params: 507 * month = 0..11 508 * date = day of month, 1..31 509 * Returns: 510 * number of days since start of epoch 511 */ 512 513 d_time makeDay(d_time year, d_time month, d_time date) 514 { 515 const y = cast(int)(year + floor(month, 12)); 516 const m = dmod(month, 12); 517 518 const leap = leapYear(y); 519 auto t = timeFromYear(y) + cast(d_time) mdays[m] * msPerDay; 520 if (leap && month >= 2) 521 t += msPerDay; 522 523 if (yearFromTime(t) != y || 524 monthFromTime(t) != m || 525 dateFromTime(t) != 1) 526 { 527 return d_time_nan; 528 } 529 530 return day(t) + date - 1; 531 } 532 533 d_time makeDate(d_time day, d_time time) 534 { 535 if (day == d_time_nan || time == d_time_nan) 536 return d_time_nan; 537 538 return day * ticksPerDay + time; 539 } 540 541 d_time timeClip(d_time time) 542 { 543 //printf("TimeClip(%g) = %g\n", time, toInteger(time)); 544 545 return toInteger(time); 546 } 547 548 /*************************************** 549 * Determine the date in the month, 1..31, of the nth 550 * weekday. 551 * Params: 552 * year = year 553 * month = month, 1..12 554 * weekday = day of week 0..6 representing Sunday..Saturday 555 * n = nth occurrence of that weekday in the month, 1..5, where 556 * 5 also means "the last occurrence in the month" 557 * Returns: 558 * the date in the month, 1..31, of the nth weekday 559 */ 560 561 int dateFromNthWeekdayOfMonth(int year, int month, int weekday, int n) 562 in 563 { 564 assert(1 <= month && month <= 12); 565 assert(0 <= weekday && weekday <= 6); 566 assert(1 <= n && n <= 5); 567 } 568 do 569 { 570 // Get day of the first of the month 571 auto x = makeDay(year, month - 1, 1); 572 573 // Get the week day 0..6 of the first of this month 574 auto wd = weekDay(makeDate(x, 0)); 575 576 // Get monthday of first occurrence of weekday in this month 577 auto mday = weekday - wd + 1; 578 if (mday < 1) 579 mday += 7; 580 581 // Add in number of weeks 582 mday += (n - 1) * 7; 583 584 // If monthday is more than the number of days in the month, 585 // back up to 'last' occurrence 586 if (mday > 28 && mday > daysInMonth(year, month)) 587 { assert(n == 5); 588 mday -= 7; 589 } 590 591 return mday; 592 } 593 594 unittest 595 { 596 assert(dateFromNthWeekdayOfMonth(2003, 3, 0, 5) == 30); 597 assert(dateFromNthWeekdayOfMonth(2003, 10, 0, 5) == 26); 598 assert(dateFromNthWeekdayOfMonth(2004, 3, 0, 5) == 28); 599 assert(dateFromNthWeekdayOfMonth(2004, 10, 0, 5) == 31); 600 } 601 602 /************************************** 603 * Determine the number of days in a month, 1..31. 604 * Params: 605 * year = Gregorian year that contains the month in question 606 * month = 1..12 607 */ 608 609 int daysInMonth(int year, int month) 610 { 611 switch (month) 612 { 613 case 1: 614 case 3: 615 case 5: 616 case 7: 617 case 8: 618 case 10: 619 case 12: 620 return 31; 621 case 2: 622 return 28 + leapYear(year); 623 case 4: 624 case 6: 625 case 9: 626 case 11: 627 return 30; 628 default: 629 break; 630 } 631 return enforce(false, "Invalid month passed to daysInMonth"); 632 } 633 634 unittest 635 { 636 assert(daysInMonth(2003, 2) == 28); 637 assert(daysInMonth(2004, 2) == 29); 638 } 639 640 /************************************* 641 * Converts UTC time into a text string of the form: 642 * "Www Mmm dd hh:mm:ss GMT+-TZ yyyy". 643 * For example, "Tue Apr 02 02:04:57 GMT-0800 1996". 644 * If time is invalid, i.e. is d_time_nan, 645 * the string "Invalid date" is returned. 646 * 647 * Example: 648 * ------------------------------------ 649 d_time lNow; 650 char[] lNowString; 651 652 // Grab the date and time relative to UTC 653 lNow = std.date.getUTCtime(); 654 // Convert this into the local date and time for display. 655 lNowString = std.date.UTCtoString(lNow); 656 * ------------------------------------ 657 */ 658 659 string UTCtoString(d_time time) 660 { 661 // Years are supposed to be -285616 .. 285616, or 7 digits 662 // "Tue Apr 02 02:04:57 GMT-0800 1996" 663 auto buffer = new char[29 + 7 + 1]; 664 665 if (time == d_time_nan) 666 return "Invalid Date"; 667 668 auto dst = daylightSavingTA(time); 669 auto offset = localTZA + dst; 670 auto t = time + offset; 671 auto sign = '+'; 672 if (offset < 0) 673 { sign = '-'; 674 // offset = -offset; 675 offset = -(localTZA + dst); 676 } 677 678 auto mn = cast(int)(offset / msPerMinute); 679 auto hr = mn / 60; 680 mn %= 60; 681 682 //printf("hr = %d, offset = %g, localTZA = %g, dst = %g, + = %g\n", hr, offset, localTZA, dst, localTZA + dst); 683 684 auto len = sprintf(buffer.ptr, 685 "%.3s %.3s %02d %02d:%02d:%02d GMT%c%02d%02d %d", 686 &daystr[weekDay(t) * 3], 687 &monstr[monthFromTime(t) * 3], 688 dateFromTime(t), 689 hourFromTime(t), minFromTime(t), secFromTime(t), 690 sign, hr, mn, 691 cast(int)yearFromTime(t)); 692 693 // Ensure no buggy buffer overflows 694 //printf("len = %d, buffer.length = %d\n", len, buffer.length); 695 assert(len < buffer.length); 696 buffer = buffer[0 .. len]; 697 return assumeUnique(buffer); 698 } 699 700 /// Alias for UTCtoString (deprecated). 701 deprecated alias UTCtoString toString; 702 703 /*********************************** 704 * Converts t into a text string of the form: "Www, dd Mmm yyyy hh:mm:ss UTC". 705 * If t is invalid, "Invalid date" is returned. 706 */ 707 708 string toUTCString(d_time t) 709 { 710 // Years are supposed to be -285616 .. 285616, or 7 digits 711 // "Tue, 02 Apr 1996 02:04:57 GMT" 712 auto buffer = new char[25 + 7 + 1]; 713 714 if (t == d_time_nan) 715 return "Invalid Date"; 716 717 auto len = sprintf(buffer.ptr, "%.3s, %02d %.3s %d %02d:%02d:%02d UTC", 718 &daystr[weekDay(t) * 3], dateFromTime(t), 719 &monstr[monthFromTime(t) * 3], 720 yearFromTime(t), 721 hourFromTime(t), minFromTime(t), secFromTime(t)); 722 723 // Ensure no buggy buffer overflows 724 assert(len < buffer.length); 725 726 return cast(string) buffer[0 .. len]; 727 } 728 729 /************************************ 730 * Converts the date portion of time into a text string of the form: "Www Mmm dd 731 * yyyy", for example, "Tue Apr 02 1996". 732 * If time is invalid, "Invalid date" is returned. 733 */ 734 735 string toDateString(d_time time) 736 { 737 // Years are supposed to be -285616 .. 285616, or 7 digits 738 // "Tue Apr 02 1996" 739 auto buffer = new char[29 + 7 + 1]; 740 741 if (time == d_time_nan) 742 return "Invalid Date"; 743 744 auto dst = daylightSavingTA(time); 745 auto offset = localTZA + dst; 746 auto t = time + offset; 747 748 auto len = sprintf(buffer.ptr, "%.3s %.3s %02d %d", 749 &daystr[weekDay(t) * 3], 750 &monstr[monthFromTime(t) * 3], 751 dateFromTime(t), 752 cast(int)yearFromTime(t)); 753 754 // Ensure no buggy buffer overflows 755 assert(len < buffer.length); 756 757 return cast(string) buffer[0 .. len]; 758 } 759 760 /****************************************** 761 * Converts the time portion of t into a text string of the form: "hh:mm:ss 762 * GMT+-TZ", for example, "02:04:57 GMT-0800". 763 * If t is invalid, "Invalid date" is returned. 764 * The input must be in UTC, and the output is in local time. 765 */ 766 767 string toTimeString(d_time time) 768 { 769 // "02:04:57 GMT-0800" 770 auto buffer = new char[17 + 1]; 771 772 if (time == d_time_nan) 773 return "Invalid Date"; 774 775 auto dst = daylightSavingTA(time); 776 auto offset = localTZA + dst; 777 auto t = time + offset; 778 auto sign = '+'; 779 if (offset < 0) 780 { sign = '-'; 781 // offset = -offset; 782 offset = -(localTZA + dst); 783 } 784 785 auto mn = cast(int)(offset / msPerMinute); 786 auto hr = mn / 60; 787 mn %= 60; 788 789 //printf("hr = %d, offset = %g, localTZA = %g, dst = %g, + = %g\n", hr, offset, localTZA, dst, localTZA + dst); 790 791 auto len = sprintf(buffer.ptr, "%02d:%02d:%02d GMT%c%02d%02d", 792 hourFromTime(t), minFromTime(t), secFromTime(t), 793 sign, hr, mn); 794 795 // Ensure no buggy buffer overflows 796 assert(len < buffer.length); 797 798 // Lop off terminating 0 799 return cast(string) buffer[0 .. len]; 800 } 801 802 803 /****************************************** 804 * Parses s as a textual date string, and returns it as a d_time. If 805 * the string is not a valid date, $(D d_time_nan) is returned. 806 */ 807 808 d_time parse(string s) 809 { 810 try 811 { 812 Date dp; 813 dp.parse(s); 814 auto time = makeTime(dp.hour, dp.minute, dp.second, dp.ms); 815 // Assume UTC if no tzcorrection is set (runnable/testdate). 816 if (dp.tzcorrection != int.min) 817 { 818 time += cast(d_time)(dp.tzcorrection / 100) * msPerHour + 819 cast(d_time)(dp.tzcorrection % 100) * msPerMinute; 820 } 821 auto day = makeDay(dp.year, dp.month - 1, dp.day); 822 auto result = makeDate(day,time); 823 return timeClip(result); 824 } 825 catch (Exception e) 826 { 827 return d_time_nan; // erroneous date string 828 } 829 } 830 831 extern(C) void std_date_static_this() 832 { 833 localTZA = getLocalTZA(); 834 } 835 836 version (Windows) 837 { 838 private import core.sys.windows.windows; 839 //import c.time; 840 841 /****** 842 * Get current UTC time. 843 */ 844 d_time getUTCtime() 845 { 846 SYSTEMTIME st; 847 GetSystemTime(&st); // get time in UTC 848 return SYSTEMTIME2d_time(&st, 0); 849 //return c.time.time(null) * ticksPerSecond; 850 } 851 852 static d_time FILETIME2d_time(const FILETIME *ft) 853 { 854 SYSTEMTIME st = void; 855 if (!FileTimeToSystemTime(ft, &st)) 856 return d_time_nan; 857 return SYSTEMTIME2d_time(&st, 0); 858 } 859 860 FILETIME d_time2FILETIME(d_time dt) 861 { 862 static assert(10_000_000 >= ticksPerSecond); 863 static assert(10_000_000 % ticksPerSecond == 0); 864 enum ulong ticksFrom1601To1970 = 11_644_473_600UL * ticksPerSecond; 865 ulong t = (dt + ticksFrom1601To1970) * (10_000_000 / ticksPerSecond); 866 FILETIME result = void; 867 result.dwLowDateTime = cast(uint) (t & uint.max); 868 result.dwHighDateTime = cast(uint) (t >> 32); 869 return result; 870 } 871 872 unittest 873 { 874 auto dt = getUTCtime(); 875 auto ft = d_time2FILETIME(dt); 876 auto dt1 = FILETIME2d_time(&ft); 877 assert(dt == dt1, text(dt, " != ", dt1)); 878 } 879 880 static d_time SYSTEMTIME2d_time(const SYSTEMTIME *st, d_time t) 881 { 882 /* More info: http://delphicikk.atw.hu/listaz.php?id=2667&oldal=52 883 */ 884 d_time day = void; 885 d_time time = void; 886 887 if (st.wYear) 888 { 889 time = makeTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); 890 day = makeDay(st.wYear, st.wMonth - 1, st.wDay); 891 } 892 else 893 { /* wYear being 0 is a flag to indicate relative time: 894 * wMonth is the month 1..12 895 * wDayOfWeek is weekday 0..6 corresponding to Sunday..Saturday 896 * wDay is the nth time, 1..5, that wDayOfWeek occurs 897 */ 898 899 auto year = yearFromTime(t); 900 auto mday = dateFromNthWeekdayOfMonth(year, 901 st.wMonth, st.wDay, st.wDayOfWeek); 902 day = makeDay(year, st.wMonth - 1, mday); 903 time = makeTime(st.wHour, st.wMinute, 0, 0); 904 } 905 auto n = makeDate(day,time); 906 return timeClip(n); 907 } 908 909 d_time getLocalTZA() 910 { 911 TIME_ZONE_INFORMATION tzi = void; 912 913 /* http://msdn.microsoft.com/library/en-us/sysinfo/base/gettimezoneinformation.asp 914 * http://msdn2.microsoft.com/en-us/library/ms725481.aspx 915 */ 916 auto r = GetTimeZoneInformation(&tzi); 917 //printf("bias = %d\n", tzi.Bias); 918 //printf("standardbias = %d\n", tzi.StandardBias); 919 //printf("daylightbias = %d\n", tzi.DaylightBias); 920 switch (r) 921 { 922 case TIME_ZONE_ID_STANDARD: 923 return -(tzi.Bias + tzi.StandardBias) 924 * cast(d_time)(60 * ticksPerSecond); 925 case TIME_ZONE_ID_DAYLIGHT: 926 // falthrough 927 //t = -(tzi.Bias + tzi.DaylightBias) * cast(d_time)(60 * ticksPerSecond); 928 //break; 929 case TIME_ZONE_ID_UNKNOWN: 930 return -(tzi.Bias) * cast(d_time)(60 * ticksPerSecond); 931 default: 932 return 0; 933 } 934 } 935 936 /* 937 * Get daylight savings time adjust for time dt. 938 */ 939 940 int daylightSavingTA(d_time dt) 941 { 942 TIME_ZONE_INFORMATION tzi = void; 943 d_time ts; 944 d_time td; 945 946 /* http://msdn.microsoft.com/library/en-us/sysinfo/base/gettimezoneinformation.asp 947 */ 948 auto r = GetTimeZoneInformation(&tzi); 949 auto t = 0; 950 switch (r) 951 { 952 case TIME_ZONE_ID_STANDARD: 953 case TIME_ZONE_ID_DAYLIGHT: 954 if (tzi.StandardDate.wMonth == 0 || 955 tzi.DaylightDate.wMonth == 0) 956 break; 957 958 ts = SYSTEMTIME2d_time(&tzi.StandardDate, dt); 959 td = SYSTEMTIME2d_time(&tzi.DaylightDate, dt); 960 961 if (td <= dt && dt < ts) 962 { 963 t = -tzi.DaylightBias * (60 * ticksPerSecond); 964 //printf("DST is in effect, %d\n", t); 965 } 966 else 967 { 968 //printf("no DST\n"); 969 } 970 break; 971 972 case TIME_ZONE_ID_UNKNOWN: 973 // Daylight savings time not used in this time zone 974 break; 975 976 default: 977 assert(0); 978 } 979 return t; 980 } 981 } 982 983 version (Posix) 984 { 985 private import core.sys.posix.time; 986 private import core.sys.posix.sys.time; 987 988 /****** 989 * Get current UTC time. 990 */ 991 d_time getUTCtime() 992 { timeval tv; 993 994 //printf("getUTCtime()\n"); 995 if (gettimeofday(&tv, null)) 996 { // Some error happened - try time() instead 997 return time(null) * ticksPerSecond; 998 } 999 1000 return tv.tv_sec * cast(d_time)ticksPerSecond + 1001 (tv.tv_usec / (1000000 / cast(d_time)ticksPerSecond)); 1002 } 1003 1004 d_time getLocalTZA() 1005 { 1006 time_t t; 1007 1008 time(&t); 1009 version (OSX) 1010 { 1011 tm result; 1012 localtime_r(&t, &result); 1013 return result.tm_gmtoff * ticksPerSecond; 1014 } 1015 else version (FreeBSD) 1016 { 1017 tm result; 1018 localtime_r(&t, &result); 1019 return result.tm_gmtoff * ticksPerSecond; 1020 } 1021 else version (OpenBSD) 1022 { 1023 tm result; 1024 localtime_r(&t, &result); 1025 return result.tm_gmtoff * ticksPerSecond; 1026 } 1027 else 1028 { 1029 localtime(&t); // this will set timezone 1030 return -(timezone * ticksPerSecond); 1031 } 1032 } 1033 1034 /* 1035 * Get daylight savings time adjust for time dt. 1036 */ 1037 1038 int daylightSavingTA(d_time dt) 1039 { 1040 tm *tmp; 1041 time_t t; 1042 int dst = 0; 1043 1044 if (dt != d_time_nan) 1045 { 1046 d_time seconds = dt / ticksPerSecond; 1047 t = cast(time_t) seconds; 1048 if (t == seconds) // if in range 1049 { 1050 tmp = localtime(&t); 1051 if (tmp.tm_isdst > 0) 1052 dst = ticksPerHour; // BUG: Assume daylight savings time is plus one hour. 1053 } 1054 else // out of range for system time, use our own calculation 1055 { 1056 /* BUG: this works for the US, but not other timezones. 1057 */ 1058 1059 dt -= localTZA; 1060 1061 int year = yearFromTime(dt); 1062 1063 /* Compute time given year, month 1..12, 1064 * week in month, weekday, hour 1065 */ 1066 d_time dstt(int year, int month, int week, int weekday, int hour) 1067 { 1068 auto mday = dateFromNthWeekdayOfMonth(year, month, weekday, week); 1069 return timeClip(makeDate( 1070 makeDay(year, month - 1, mday), 1071 makeTime(hour, 0, 0, 0))); 1072 } 1073 1074 d_time start; 1075 d_time end; 1076 if (year < 2007) 1077 { // Daylight savings time goes from 2 AM the first Sunday 1078 // in April through 2 AM the last Sunday in October 1079 start = dstt(year, 4, 1, 0, 2); 1080 end = dstt(year, 10, 5, 0, 2); 1081 } 1082 else 1083 { 1084 // the second Sunday of March to 1085 // the first Sunday in November 1086 start = dstt(year, 3, 2, 0, 2); 1087 end = dstt(year, 11, 1, 0, 2); 1088 } 1089 1090 if (start <= dt && dt < end) 1091 dst = ticksPerHour; 1092 //writefln("start = %s, dt = %s, end = %s, dst = %s", start, dt, end, dst); 1093 } 1094 } 1095 return dst; 1096 } 1097 1098 } 1099 1100 1101 /+ DOS File Time +/ 1102 1103 /*** 1104 * Type representing the DOS file date/time format. 1105 */ 1106 alias uint DosFileTime; 1107 1108 /************************************ 1109 * Convert from DOS file date/time to d_time. 1110 */ 1111 1112 d_time toDtime(DosFileTime time) 1113 { 1114 uint dt = cast(uint)time; 1115 1116 if (dt == 0) 1117 return d_time_nan; 1118 1119 int year = ((dt >> 25) & 0x7F) + 1980; 1120 int month = ((dt >> 21) & 0x0F) - 1; // 0..12 1121 int dayofmonth = ((dt >> 16) & 0x1F); // 0..31 1122 int hour = (dt >> 11) & 0x1F; // 0..23 1123 int minute = (dt >> 5) & 0x3F; // 0..59 1124 int second = (dt << 1) & 0x3E; // 0..58 (in 2 second increments) 1125 1126 d_time t; 1127 1128 t = undead.date.makeDate(undead.date.makeDay(year, month, dayofmonth), 1129 undead.date.makeTime(hour, minute, second, 0)); 1130 1131 assert(yearFromTime(t) == year); 1132 assert(monthFromTime(t) == month); 1133 assert(dateFromTime(t) == dayofmonth); 1134 assert(hourFromTime(t) == hour); 1135 assert(minFromTime(t) == minute); 1136 assert(secFromTime(t) == second); 1137 1138 t -= localTZA + daylightSavingTA(t); 1139 1140 return t; 1141 } 1142 1143 /**************************************** 1144 * Convert from d_time to DOS file date/time. 1145 */ 1146 1147 DosFileTime toDosFileTime(d_time t) 1148 { uint dt; 1149 1150 if (t == d_time_nan) 1151 return cast(DosFileTime)0; 1152 1153 t += localTZA + daylightSavingTA(t); 1154 1155 uint year = yearFromTime(t); 1156 uint month = monthFromTime(t); 1157 uint dayofmonth = dateFromTime(t); 1158 uint hour = hourFromTime(t); 1159 uint minute = minFromTime(t); 1160 uint second = secFromTime(t); 1161 1162 dt = (year - 1980) << 25; 1163 dt |= ((month + 1) & 0x0F) << 21; 1164 dt |= (dayofmonth & 0x1F) << 16; 1165 dt |= (hour & 0x1F) << 11; 1166 dt |= (minute & 0x3F) << 5; 1167 dt |= (second >> 1) & 0x1F; 1168 1169 return cast(DosFileTime)dt; 1170 } 1171 1172 /** 1173 Benchmarks code for speed assessment and comparison. 1174 1175 Params: 1176 1177 fun = aliases of callable objects (e.g. function names). Each should 1178 take no arguments. 1179 1180 times = The number of times each function is to be executed. 1181 1182 result = The optional store for the return value. If $(D null) is 1183 passed in, new store is allocated appropriately. 1184 1185 Returns: 1186 1187 An array of $(D n) $(D uint)s. Element at slot $(D i) contains the 1188 number of milliseconds spent in calling the $(D i)th function $(D 1189 times) times. 1190 1191 Example: 1192 ---- 1193 int a; 1194 void f0() { } 1195 void f1() { auto b = a; } 1196 void f2() { auto b = to!(string)(a); } 1197 auto r = benchmark!(f0, f1, f2)(10_000_000); 1198 ---- 1199 */ 1200 ulong[] benchmark(fun...)(uint times, ulong[] result = null) 1201 { 1202 result.length = fun.length; 1203 result.length = 0; 1204 foreach (i, Unused; fun) 1205 { 1206 immutable t = getUTCtime(); 1207 foreach (j; 0 .. times) 1208 { 1209 fun[i](); 1210 } 1211 immutable delta = getUTCtime() - t; 1212 result ~= cast(uint)delta; 1213 } 1214 foreach (ref e; result) 1215 { 1216 e *= 1000; 1217 e /= ticksPerSecond; 1218 } 1219 return result; 1220 } 1221 1222 unittest 1223 { 1224 int a; 1225 void f0() { } 1226 //void f1() { auto b = to!(string)(a); } 1227 void f2() { auto b = (a); } 1228 auto r = benchmark!(f0, f2)(100); 1229 //writeln(r); 1230 }