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 * month = 1..12 606 */ 607 608 int daysInMonth(int year, int month) 609 { 610 switch (month) 611 { 612 case 1: 613 case 3: 614 case 5: 615 case 7: 616 case 8: 617 case 10: 618 case 12: 619 return 31; 620 case 2: 621 return 28 + leapYear(year); 622 case 4: 623 case 6: 624 case 9: 625 case 11: 626 return 30; 627 default: 628 break; 629 } 630 return enforce(false, "Invalid month passed to daysInMonth"); 631 } 632 633 unittest 634 { 635 assert(daysInMonth(2003, 2) == 28); 636 assert(daysInMonth(2004, 2) == 29); 637 } 638 639 /************************************* 640 * Converts UTC time into a text string of the form: 641 * "Www Mmm dd hh:mm:ss GMT+-TZ yyyy". 642 * For example, "Tue Apr 02 02:04:57 GMT-0800 1996". 643 * If time is invalid, i.e. is d_time_nan, 644 * the string "Invalid date" is returned. 645 * 646 * Example: 647 * ------------------------------------ 648 d_time lNow; 649 char[] lNowString; 650 651 // Grab the date and time relative to UTC 652 lNow = std.date.getUTCtime(); 653 // Convert this into the local date and time for display. 654 lNowString = std.date.UTCtoString(lNow); 655 * ------------------------------------ 656 */ 657 658 string UTCtoString(d_time time) 659 { 660 // Years are supposed to be -285616 .. 285616, or 7 digits 661 // "Tue Apr 02 02:04:57 GMT-0800 1996" 662 auto buffer = new char[29 + 7 + 1]; 663 664 if (time == d_time_nan) 665 return "Invalid Date"; 666 667 auto dst = daylightSavingTA(time); 668 auto offset = localTZA + dst; 669 auto t = time + offset; 670 auto sign = '+'; 671 if (offset < 0) 672 { sign = '-'; 673 // offset = -offset; 674 offset = -(localTZA + dst); 675 } 676 677 auto mn = cast(int)(offset / msPerMinute); 678 auto hr = mn / 60; 679 mn %= 60; 680 681 //printf("hr = %d, offset = %g, localTZA = %g, dst = %g, + = %g\n", hr, offset, localTZA, dst, localTZA + dst); 682 683 auto len = sprintf(buffer.ptr, 684 "%.3s %.3s %02d %02d:%02d:%02d GMT%c%02d%02d %d", 685 &daystr[weekDay(t) * 3], 686 &monstr[monthFromTime(t) * 3], 687 dateFromTime(t), 688 hourFromTime(t), minFromTime(t), secFromTime(t), 689 sign, hr, mn, 690 cast(int)yearFromTime(t)); 691 692 // Ensure no buggy buffer overflows 693 //printf("len = %d, buffer.length = %d\n", len, buffer.length); 694 assert(len < buffer.length); 695 buffer = buffer[0 .. len]; 696 return assumeUnique(buffer); 697 } 698 699 /// Alias for UTCtoString (deprecated). 700 deprecated alias UTCtoString toString; 701 702 /*********************************** 703 * Converts t into a text string of the form: "Www, dd Mmm yyyy hh:mm:ss UTC". 704 * If t is invalid, "Invalid date" is returned. 705 */ 706 707 string toUTCString(d_time t) 708 { 709 // Years are supposed to be -285616 .. 285616, or 7 digits 710 // "Tue, 02 Apr 1996 02:04:57 GMT" 711 auto buffer = new char[25 + 7 + 1]; 712 713 if (t == d_time_nan) 714 return "Invalid Date"; 715 716 auto len = sprintf(buffer.ptr, "%.3s, %02d %.3s %d %02d:%02d:%02d UTC", 717 &daystr[weekDay(t) * 3], dateFromTime(t), 718 &monstr[monthFromTime(t) * 3], 719 yearFromTime(t), 720 hourFromTime(t), minFromTime(t), secFromTime(t)); 721 722 // Ensure no buggy buffer overflows 723 assert(len < buffer.length); 724 725 return cast(string) buffer[0 .. len]; 726 } 727 728 /************************************ 729 * Converts the date portion of time into a text string of the form: "Www Mmm dd 730 * yyyy", for example, "Tue Apr 02 1996". 731 * If time is invalid, "Invalid date" is returned. 732 */ 733 734 string toDateString(d_time time) 735 { 736 // Years are supposed to be -285616 .. 285616, or 7 digits 737 // "Tue Apr 02 1996" 738 auto buffer = new char[29 + 7 + 1]; 739 740 if (time == d_time_nan) 741 return "Invalid Date"; 742 743 auto dst = daylightSavingTA(time); 744 auto offset = localTZA + dst; 745 auto t = time + offset; 746 747 auto len = sprintf(buffer.ptr, "%.3s %.3s %02d %d", 748 &daystr[weekDay(t) * 3], 749 &monstr[monthFromTime(t) * 3], 750 dateFromTime(t), 751 cast(int)yearFromTime(t)); 752 753 // Ensure no buggy buffer overflows 754 assert(len < buffer.length); 755 756 return cast(string) buffer[0 .. len]; 757 } 758 759 /****************************************** 760 * Converts the time portion of t into a text string of the form: "hh:mm:ss 761 * GMT+-TZ", for example, "02:04:57 GMT-0800". 762 * If t is invalid, "Invalid date" is returned. 763 * The input must be in UTC, and the output is in local time. 764 */ 765 766 string toTimeString(d_time time) 767 { 768 // "02:04:57 GMT-0800" 769 auto buffer = new char[17 + 1]; 770 771 if (time == d_time_nan) 772 return "Invalid Date"; 773 774 auto dst = daylightSavingTA(time); 775 auto offset = localTZA + dst; 776 auto t = time + offset; 777 auto sign = '+'; 778 if (offset < 0) 779 { sign = '-'; 780 // offset = -offset; 781 offset = -(localTZA + dst); 782 } 783 784 auto mn = cast(int)(offset / msPerMinute); 785 auto hr = mn / 60; 786 mn %= 60; 787 788 //printf("hr = %d, offset = %g, localTZA = %g, dst = %g, + = %g\n", hr, offset, localTZA, dst, localTZA + dst); 789 790 auto len = sprintf(buffer.ptr, "%02d:%02d:%02d GMT%c%02d%02d", 791 hourFromTime(t), minFromTime(t), secFromTime(t), 792 sign, hr, mn); 793 794 // Ensure no buggy buffer overflows 795 assert(len < buffer.length); 796 797 // Lop off terminating 0 798 return cast(string) buffer[0 .. len]; 799 } 800 801 802 /****************************************** 803 * Parses s as a textual date string, and returns it as a d_time. If 804 * the string is not a valid date, $(D d_time_nan) is returned. 805 */ 806 807 d_time parse(string s) 808 { 809 try 810 { 811 Date dp; 812 dp.parse(s); 813 auto time = makeTime(dp.hour, dp.minute, dp.second, dp.ms); 814 // Assume UTC if no tzcorrection is set (runnable/testdate). 815 if (dp.tzcorrection != int.min) 816 { 817 time += cast(d_time)(dp.tzcorrection / 100) * msPerHour + 818 cast(d_time)(dp.tzcorrection % 100) * msPerMinute; 819 } 820 auto day = makeDay(dp.year, dp.month - 1, dp.day); 821 auto result = makeDate(day,time); 822 return timeClip(result); 823 } 824 catch (Exception e) 825 { 826 return d_time_nan; // erroneous date string 827 } 828 } 829 830 extern(C) void std_date_static_this() 831 { 832 localTZA = getLocalTZA(); 833 } 834 835 version (Windows) 836 { 837 private import core.sys.windows.windows; 838 //import c.time; 839 840 /****** 841 * Get current UTC time. 842 */ 843 d_time getUTCtime() 844 { 845 SYSTEMTIME st; 846 GetSystemTime(&st); // get time in UTC 847 return SYSTEMTIME2d_time(&st, 0); 848 //return c.time.time(null) * ticksPerSecond; 849 } 850 851 static d_time FILETIME2d_time(const FILETIME *ft) 852 { 853 SYSTEMTIME st = void; 854 if (!FileTimeToSystemTime(ft, &st)) 855 return d_time_nan; 856 return SYSTEMTIME2d_time(&st, 0); 857 } 858 859 FILETIME d_time2FILETIME(d_time dt) 860 { 861 static assert(10_000_000 >= ticksPerSecond); 862 static assert(10_000_000 % ticksPerSecond == 0); 863 enum ulong ticksFrom1601To1970 = 11_644_473_600UL * ticksPerSecond; 864 ulong t = (dt + ticksFrom1601To1970) * (10_000_000 / ticksPerSecond); 865 FILETIME result = void; 866 result.dwLowDateTime = cast(uint) (t & uint.max); 867 result.dwHighDateTime = cast(uint) (t >> 32); 868 return result; 869 } 870 871 unittest 872 { 873 auto dt = getUTCtime(); 874 auto ft = d_time2FILETIME(dt); 875 auto dt1 = FILETIME2d_time(&ft); 876 assert(dt == dt1, text(dt, " != ", dt1)); 877 } 878 879 static d_time SYSTEMTIME2d_time(const SYSTEMTIME *st, d_time t) 880 { 881 /* More info: http://delphicikk.atw.hu/listaz.php?id=2667&oldal=52 882 */ 883 d_time day = void; 884 d_time time = void; 885 886 if (st.wYear) 887 { 888 time = makeTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); 889 day = makeDay(st.wYear, st.wMonth - 1, st.wDay); 890 } 891 else 892 { /* wYear being 0 is a flag to indicate relative time: 893 * wMonth is the month 1..12 894 * wDayOfWeek is weekday 0..6 corresponding to Sunday..Saturday 895 * wDay is the nth time, 1..5, that wDayOfWeek occurs 896 */ 897 898 auto year = yearFromTime(t); 899 auto mday = dateFromNthWeekdayOfMonth(year, 900 st.wMonth, st.wDay, st.wDayOfWeek); 901 day = makeDay(year, st.wMonth - 1, mday); 902 time = makeTime(st.wHour, st.wMinute, 0, 0); 903 } 904 auto n = makeDate(day,time); 905 return timeClip(n); 906 } 907 908 d_time getLocalTZA() 909 { 910 TIME_ZONE_INFORMATION tzi = void; 911 912 /* http://msdn.microsoft.com/library/en-us/sysinfo/base/gettimezoneinformation.asp 913 * http://msdn2.microsoft.com/en-us/library/ms725481.aspx 914 */ 915 auto r = GetTimeZoneInformation(&tzi); 916 //printf("bias = %d\n", tzi.Bias); 917 //printf("standardbias = %d\n", tzi.StandardBias); 918 //printf("daylightbias = %d\n", tzi.DaylightBias); 919 switch (r) 920 { 921 case TIME_ZONE_ID_STANDARD: 922 return -(tzi.Bias + tzi.StandardBias) 923 * cast(d_time)(60 * ticksPerSecond); 924 case TIME_ZONE_ID_DAYLIGHT: 925 // falthrough 926 //t = -(tzi.Bias + tzi.DaylightBias) * cast(d_time)(60 * ticksPerSecond); 927 //break; 928 case TIME_ZONE_ID_UNKNOWN: 929 return -(tzi.Bias) * cast(d_time)(60 * ticksPerSecond); 930 default: 931 return 0; 932 } 933 } 934 935 /* 936 * Get daylight savings time adjust for time dt. 937 */ 938 939 int daylightSavingTA(d_time dt) 940 { 941 TIME_ZONE_INFORMATION tzi = void; 942 d_time ts; 943 d_time td; 944 945 /* http://msdn.microsoft.com/library/en-us/sysinfo/base/gettimezoneinformation.asp 946 */ 947 auto r = GetTimeZoneInformation(&tzi); 948 auto t = 0; 949 switch (r) 950 { 951 case TIME_ZONE_ID_STANDARD: 952 case TIME_ZONE_ID_DAYLIGHT: 953 if (tzi.StandardDate.wMonth == 0 || 954 tzi.DaylightDate.wMonth == 0) 955 break; 956 957 ts = SYSTEMTIME2d_time(&tzi.StandardDate, dt); 958 td = SYSTEMTIME2d_time(&tzi.DaylightDate, dt); 959 960 if (td <= dt && dt < ts) 961 { 962 t = -tzi.DaylightBias * (60 * ticksPerSecond); 963 //printf("DST is in effect, %d\n", t); 964 } 965 else 966 { 967 //printf("no DST\n"); 968 } 969 break; 970 971 case TIME_ZONE_ID_UNKNOWN: 972 // Daylight savings time not used in this time zone 973 break; 974 975 default: 976 assert(0); 977 } 978 return t; 979 } 980 } 981 982 version (Posix) 983 { 984 private import core.sys.posix.time; 985 private import core.sys.posix.sys.time; 986 987 /****** 988 * Get current UTC time. 989 */ 990 d_time getUTCtime() 991 { timeval tv; 992 993 //printf("getUTCtime()\n"); 994 if (gettimeofday(&tv, null)) 995 { // Some error happened - try time() instead 996 return time(null) * ticksPerSecond; 997 } 998 999 return tv.tv_sec * cast(d_time)ticksPerSecond + 1000 (tv.tv_usec / (1000000 / cast(d_time)ticksPerSecond)); 1001 } 1002 1003 d_time getLocalTZA() 1004 { 1005 time_t t; 1006 1007 time(&t); 1008 version (OSX) 1009 { 1010 tm result; 1011 localtime_r(&t, &result); 1012 return result.tm_gmtoff * ticksPerSecond; 1013 } 1014 else version (FreeBSD) 1015 { 1016 tm result; 1017 localtime_r(&t, &result); 1018 return result.tm_gmtoff * ticksPerSecond; 1019 } 1020 else 1021 { 1022 localtime(&t); // this will set timezone 1023 return -(timezone * ticksPerSecond); 1024 } 1025 } 1026 1027 /* 1028 * Get daylight savings time adjust for time dt. 1029 */ 1030 1031 int daylightSavingTA(d_time dt) 1032 { 1033 tm *tmp; 1034 time_t t; 1035 int dst = 0; 1036 1037 if (dt != d_time_nan) 1038 { 1039 d_time seconds = dt / ticksPerSecond; 1040 t = cast(time_t) seconds; 1041 if (t == seconds) // if in range 1042 { 1043 tmp = localtime(&t); 1044 if (tmp.tm_isdst > 0) 1045 dst = ticksPerHour; // BUG: Assume daylight savings time is plus one hour. 1046 } 1047 else // out of range for system time, use our own calculation 1048 { 1049 /* BUG: this works for the US, but not other timezones. 1050 */ 1051 1052 dt -= localTZA; 1053 1054 int year = yearFromTime(dt); 1055 1056 /* Compute time given year, month 1..12, 1057 * week in month, weekday, hour 1058 */ 1059 d_time dstt(int year, int month, int week, int weekday, int hour) 1060 { 1061 auto mday = dateFromNthWeekdayOfMonth(year, month, weekday, week); 1062 return timeClip(makeDate( 1063 makeDay(year, month - 1, mday), 1064 makeTime(hour, 0, 0, 0))); 1065 } 1066 1067 d_time start; 1068 d_time end; 1069 if (year < 2007) 1070 { // Daylight savings time goes from 2 AM the first Sunday 1071 // in April through 2 AM the last Sunday in October 1072 start = dstt(year, 4, 1, 0, 2); 1073 end = dstt(year, 10, 5, 0, 2); 1074 } 1075 else 1076 { 1077 // the second Sunday of March to 1078 // the first Sunday in November 1079 start = dstt(year, 3, 2, 0, 2); 1080 end = dstt(year, 11, 1, 0, 2); 1081 } 1082 1083 if (start <= dt && dt < end) 1084 dst = ticksPerHour; 1085 //writefln("start = %s, dt = %s, end = %s, dst = %s", start, dt, end, dst); 1086 } 1087 } 1088 return dst; 1089 } 1090 1091 } 1092 1093 1094 /+ DOS File Time +/ 1095 1096 /*** 1097 * Type representing the DOS file date/time format. 1098 */ 1099 alias uint DosFileTime; 1100 1101 /************************************ 1102 * Convert from DOS file date/time to d_time. 1103 */ 1104 1105 d_time toDtime(DosFileTime time) 1106 { 1107 uint dt = cast(uint)time; 1108 1109 if (dt == 0) 1110 return d_time_nan; 1111 1112 int year = ((dt >> 25) & 0x7F) + 1980; 1113 int month = ((dt >> 21) & 0x0F) - 1; // 0..12 1114 int dayofmonth = ((dt >> 16) & 0x1F); // 0..31 1115 int hour = (dt >> 11) & 0x1F; // 0..23 1116 int minute = (dt >> 5) & 0x3F; // 0..59 1117 int second = (dt << 1) & 0x3E; // 0..58 (in 2 second increments) 1118 1119 d_time t; 1120 1121 t = undead.date.makeDate(undead.date.makeDay(year, month, dayofmonth), 1122 undead.date.makeTime(hour, minute, second, 0)); 1123 1124 assert(yearFromTime(t) == year); 1125 assert(monthFromTime(t) == month); 1126 assert(dateFromTime(t) == dayofmonth); 1127 assert(hourFromTime(t) == hour); 1128 assert(minFromTime(t) == minute); 1129 assert(secFromTime(t) == second); 1130 1131 t -= localTZA + daylightSavingTA(t); 1132 1133 return t; 1134 } 1135 1136 /**************************************** 1137 * Convert from d_time to DOS file date/time. 1138 */ 1139 1140 DosFileTime toDosFileTime(d_time t) 1141 { uint dt; 1142 1143 if (t == d_time_nan) 1144 return cast(DosFileTime)0; 1145 1146 t += localTZA + daylightSavingTA(t); 1147 1148 uint year = yearFromTime(t); 1149 uint month = monthFromTime(t); 1150 uint dayofmonth = dateFromTime(t); 1151 uint hour = hourFromTime(t); 1152 uint minute = minFromTime(t); 1153 uint second = secFromTime(t); 1154 1155 dt = (year - 1980) << 25; 1156 dt |= ((month + 1) & 0x0F) << 21; 1157 dt |= (dayofmonth & 0x1F) << 16; 1158 dt |= (hour & 0x1F) << 11; 1159 dt |= (minute & 0x3F) << 5; 1160 dt |= (second >> 1) & 0x1F; 1161 1162 return cast(DosFileTime)dt; 1163 } 1164 1165 /** 1166 Benchmarks code for speed assessment and comparison. 1167 1168 Params: 1169 1170 fun = aliases of callable objects (e.g. function names). Each should 1171 take no arguments. 1172 1173 times = The number of times each function is to be executed. 1174 1175 result = The optional store for the return value. If $(D null) is 1176 passed in, new store is allocated appropriately. 1177 1178 Returns: 1179 1180 An array of $(D n) $(D uint)s. Element at slot $(D i) contains the 1181 number of milliseconds spent in calling the $(D i)th function $(D 1182 times) times. 1183 1184 Example: 1185 ---- 1186 int a; 1187 void f0() { } 1188 void f1() { auto b = a; } 1189 void f2() { auto b = to!(string)(a); } 1190 auto r = benchmark!(f0, f1, f2)(10_000_000); 1191 ---- 1192 */ 1193 ulong[] benchmark(fun...)(uint times, ulong[] result = null) 1194 { 1195 result.length = fun.length; 1196 result.length = 0; 1197 foreach (i, Unused; fun) 1198 { 1199 immutable t = getUTCtime(); 1200 foreach (j; 0 .. times) 1201 { 1202 fun[i](); 1203 } 1204 immutable delta = getUTCtime() - t; 1205 result ~= cast(uint)delta; 1206 } 1207 foreach (ref e; result) 1208 { 1209 e *= 1000; 1210 e /= ticksPerSecond; 1211 } 1212 return result; 1213 } 1214 1215 unittest 1216 { 1217 int a; 1218 void f0() { } 1219 //void f1() { auto b = to!(string)(a); } 1220 void f2() { auto b = (a); } 1221 auto r = benchmark!(f0, f2)(100); 1222 //writeln(r); 1223 }