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