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 }