root/trunk/bevutils/date.d

Revision 20, 25.2 kB (checked in by teales, 1 year ago)

Bevutils files initial check-in.

Line 
1 // Written in the D programming language.
2
3 /**
4  * Dates are represented in several formats. The date implementation revolves
5  * around a central type, d_time, from which other formats are converted to and
6  * from.
7  * Dates are calculated using the Gregorian calendar.
8  * References:
9  *  $(LINK2 http://en.wikipedia.org/wiki/Gregorian_calendar, Gregorian calendar (Wikipedia))
10  * Macros:
11  *  WIKI = Phobos/StdDate
12  */
13
14 // Copyright (c) 1999-2006 by Digital Mars
15 // All Rights Reserved
16 // written by Walter Bright
17 // www.digitalmars.com
18
19 module std.date;
20
21 private import std.stdio;
22 private import std.dateparse;
23
24 /**
25  * d_time is a signed arithmetic type giving the time elapsed since January 1,
26  * 1970.
27  * Negative values are for dates preceding 1970. The time unit used is Ticks.
28  * Ticks are milliseconds or smaller intervals.
29  *
30  * The usual arithmetic operations can be performed on d_time, such as adding,
31  * subtracting, etc. Elapsed time in Ticks can be computed by subtracting a
32  * starting d_time from an ending d_time.
33  */
34 alias long d_time;
35
36 /**
37  * A value for d_time that does not represent a valid time.
38  */
39 const d_time d_time_nan = long.min;
40
41 /**
42  * Time broken down into its components.
43  */
44 struct Date
45 {
46     int year = int.min; /// use int.min as "nan" year value
47     int month;         /// 1..12
48     int day;           /// 1..31
49     int hour;          /// 0..23
50     int minute;     /// 0..59
51     int second;     /// 0..59
52     int ms;           /// 0..999
53     int weekday;       /// 0: not specified, 1..7: Sunday..Saturday
54     int yday;        /// 1..365(366)
55     int tzcorrection = int.min; /// -1200..1200 correction in hours
56
57     void fromTicks(d_time t)
58     {   
59         int y;
60         int d;
61         int m;
62
63         if (t == d_time_nan)
64             throw new Exception("Bad d_time value");
65
66         // Hazard a guess
67         y = 1970 + cast(int) (t / (3652425 * (msPerDay / 10000)));
68
69         if (TimeFromYear(y) <= t)
70         {
71             while (TimeFromYear(y + 1) <= t)
72                 y++;
73         }
74         else
75         {
76             do
77             {
78                 y--;
79             } while (TimeFromYear(y) > t);
80         }
81         year = y;
82
83         int leap = LeapYear(y);
84         d = Day(t) - DayFromYear(year);
85         yday = d;
86
87         if (d < 59)
88         {
89             if (d < 31)
90             {
91                 assert(d >= 0);
92                 m = 0;
93             }
94             else
95             {
96                 m = 1;
97             }
98         }
99         else
100         {
101             d -= leap;
102             if (d < 212)
103             {
104                 if (d < 59)
105                     m = 1;
106                 else if (d < 90)
107                     m = 2;
108                 else if (d < 120)
109                     m = 3;
110                 else if (d < 151)
111                     m = 4;
112                 else if (d < 181)
113                     m = 5;
114                 else
115                     m = 6;
116             }
117             else
118             {
119                 if (d < 243)
120                     m = 7;
121                 else if (d < 273)
122                     m = 8;
123                 else if (d < 304)
124                     m = 9;
125                 else if (d < 334)
126                     m = 10;
127                 else if (d < 365)
128                     m = 11;
129                 else
130                     assert(0);
131             }
132         }
133         month = m+1;
134         switch (m)
135         {
136             case 0:  day = d +   1;      break;
137             case 1:  day = d -  30;      break;
138             case 2:  day = d -  58 - leap;   break;
139             case 3:  day = d -  89 - leap;   break;
140             case 4:  day = d - 119 - leap;   break;
141             case 5:  day = d - 150 - leap;   break;
142             case 6:  day = d - 180 - leap;   break;
143             case 7:  day = d - 211 - leap;   break;
144             case 8:  day = d - 242 - leap;   break;
145             case 9:  day = d - 272 - leap;   break;
146             case 10: day = d - 303 - leap;   break;
147             case 11: day = d - 333 - leap;   break;
148             default:
149                 assert(0);
150         }
151         hour = HourFromTime(t);
152         minute = MinFromTime(t);
153         second = SecFromTime(t);
154         ms = msFromTime(t);
155         weekday = WeekDay(t);
156     }
157
158     /// Parse date out of string s[] and store it in this Date instance.
159     void parse(char[] s)
160     {
161          DateParse dp;
162          dp.parse(s, *this);
163     }
164 }
165
166 enum
167 {
168     HoursPerDay    = 24,
169     MinutesPerHour = 60,
170     msPerMinute    = 60 * 1000,
171     msPerHour      = 60 * msPerMinute,
172     msPerDay       = 86400000,
173     TicksPerMs     = 1,
174     TicksPerSecond = 1000,          /// Will be at least 1000
175     TicksPerMinute = TicksPerSecond * 60,
176     TicksPerHour   = TicksPerMinute * 60,
177     TicksPerDay    = TicksPerHour   * 24,
178 }
179
180 d_time LocalTZA = 0;
181
182
183 const char[] daystr = "SunMonTueWedThuFriSat";
184 const char[] monstr = "JanFebMarAprMayJunJulAugSepOctNovDec";
185
186 const int[12] mdays = [ 0,31,59,90,120,151,181,212,243,273,304,334 ];
187
188 /********************************
189  * Compute year and week [1..53] from t. The ISO 8601 week 1 is the first week
190  * of the year that includes January 4. Monday is the first day of the week.
191  * References:
192  *  $(LINK2 http://en.wikipedia.org/wiki/ISO_8601, ISO 8601 (Wikipedia))
193  */
194
195 void toISO8601YearWeek(d_time t, out int year, out int week)
196 {
197     year = YearFromTime(t);
198
199     int yday = Day(t) - DayFromYear(year);
200     int d;
201     int w;
202     int ydaybeg;
203
204     /* Determine day of week Jan 4 falls on.
205      * Weeks begin on a Monday.
206      */
207
208     d = DayFromYear(year);
209     w = (d + 3/*Jan4*/ + 3) % 7;
210     if (w < 0)
211         w += 7;
212
213     /* Find yday of beginning of ISO 8601 year
214      */
215     ydaybeg = 3/*Jan4*/ - w;
216
217     /* Check if yday is actually the last week of the previous year
218      */
219     if (yday < ydaybeg)
220     {
221     year -= 1;
222     week = 53;
223         return;
224     }
225
226     /* Check if yday is actually the first week of the next year
227      */
228     if (yday >= 362)                            // possible
229     {   int d2;
230         int ydaybeg2;
231
232         d2 = DayFromYear(year + 1);
233         w = (d2 + 3/*Jan4*/ + 3) % 7;
234         if (w < 0)
235             w += 7;
236         //printf("w = %d\n", w);
237         ydaybeg2 = 3/*Jan4*/ - w;
238         if (d + yday >= d2 + ydaybeg2)
239         {
240         year += 1;
241         week = 1;
242             return;
243         }
244     }
245
246     week = (yday - ydaybeg) / 7 + 1;
247 }
248
249 /* ***********************************
250  * Divide time by divisor. Always round down, even if d is negative.
251  */
252
253 d_time floor(d_time d, int divisor)
254 {
255     if (d < 0)
256     d -= divisor - 1;
257     return d / divisor;
258 }
259
260 int dmod(d_time n, d_time d)
261 {   d_time r;
262
263     r = n % d;
264     if (r < 0)
265     r += d;
266     assert(cast(int)r == r);
267     return cast(int)r;
268 }
269
270 int HourFromTime(d_time t)
271 {
272     return dmod(floor(t, msPerHour), HoursPerDay);
273 }
274
275 int MinFromTime(d_time t)
276 {
277     return dmod(floor(t, msPerMinute), MinutesPerHour);
278 }
279
280 int SecFromTime(d_time t)
281 {
282     return dmod(floor(t, TicksPerSecond), 60);
283 }
284
285 int msFromTime(d_time t)
286 {
287     return dmod(t / (TicksPerSecond / 1000), 1000);
288 }
289
290 int TimeWithinDay(d_time t)
291 {
292     return dmod(t, msPerDay);
293 }
294
295 d_time toInteger(d_time n)
296 {
297     return n;
298 }
299
300 int Day(d_time t)
301 {
302     return cast(int)floor(t, msPerDay);
303 }
304
305 int LeapYear(int y)
306 {
307     return ((y & 3) == 0 &&
308         (y % 100 || (y % 400) == 0));
309 }
310
311 int DaysInYear(int y)
312 {
313     return 365 + LeapYear(y);
314 }
315
316 int DayFromYear(int y)
317 {
318     return cast(int) (365 * (y - 1970) +
319         floor((y - 1969), 4) -
320         floor((y - 1901), 100) +
321         floor((y - 1601), 400));
322 }
323
324 d_time TimeFromYear(int y)
325 {
326     return cast(d_time)msPerDay * DayFromYear(y);
327 }
328
329 /*****************************
330  * Calculates the year from the d_time t.
331  */
332
333 int YearFromTime(d_time t)
334 {  
335     int y;
336
337     if (t == d_time_nan)
338         return 0;
339
340     // Hazard a guess
341     // y = 1970 + cast(int) (t / (365.2425 * msPerDay));
342     // Use integer only math
343     y = 1970 + cast(int) (t / (3652425 * (msPerDay / 10000)));
344
345     if (TimeFromYear(y) <= t)
346     {
347          while (TimeFromYear(y + 1) <= t)
348              y++;
349     }
350     else
351     {
352          do
353          {
354              y--;
355          } while (TimeFromYear(y) > t);
356     }
357     return y;
358 }
359
360 /*******************************
361  * Determines if d_time t is a leap year.
362  *
363  * A leap year is every 4 years except years ending in 00 that are not
364  * divsible by 400.
365  *
366  * Returns: !=0 if it is a leap year.
367  *
368  * References:
369  *  $(LINK2 http://en.wikipedia.org/wiki/Leap_year, Wikipedia)
370  */
371
372 int inLeapYear(d_time t)
373 {
374     return LeapYear(YearFromTime(t));
375 }
376
377 /*****************************
378  * Calculates the month from the d_time t.
379  *
380  * Returns: Integer in the range 0..11, where
381  *  0 represents January and 11 represents December.
382  */
383
384 int MonthFromTime(d_time t)
385 {
386     int day;
387     int month;
388     int year;
389
390     year = YearFromTime(t);
391     day = Day(t) - DayFromYear(year);
392
393     if (day < 59)
394     {
395     if (day < 31)
396     {   assert(day >= 0);
397         month = 0;
398     }
399     else
400         month = 1;
401     }
402     else
403     {
404     day -= LeapYear(year);
405     if (day < 212)
406     {
407         if (day < 59)
408         month = 1;
409         else if (day < 90)
410         month = 2;
411         else if (day < 120)
412         month = 3;
413         else if (day < 151)
414         month = 4;
415         else if (day < 181)
416         month = 5;
417         else
418         month = 6;
419     }
420     else
421     {
422         if (day < 243)
423         month = 7;
424         else if (day < 273)
425         month = 8;
426         else if (day < 304)
427         month = 9;
428         else if (day < 334)
429         month = 10;
430         else if (day < 365)
431         month = 11;
432         else
433         assert(0);
434     }
435     }
436     return month;
437 }
438
439 /*******************************
440  * Compute which day in a month a d_time t is.
441  * Returns:
442  *  Integer in the range 1..31
443  */
444 int DateFromTime(d_time t)
445 {
446     int day;
447     int leap;
448     int month;
449     int year;
450     int date;
451
452     year = YearFromTime(t);
453     day = Day(t) - DayFromYear(year);
454     leap = LeapYear(year);
455     month = MonthFromTime(t);
456     switch (month)
457     {
458     case 0:  date = day +   1;      break;
459     case 1:  date = day -  30;      break;
460     case 2:  date = day -  58 - leap;   break;
461     case 3:  date = day -  89 - leap;   break;
462     case 4:  date = day - 119 - leap;   break;
463     case 5:  date = day - 150 - leap;   break;
464     case 6:  date = day - 180 - leap;   break;
465     case 7:  date = day - 211 - leap;   break;
466     case 8:  date = day - 242 - leap;   break;
467     case 9:  date = day - 272 - leap;   break;
468     case 10: date = day - 303 - leap;   break;
469     case 11: date = day - 333 - leap;   break;
470     default:
471         assert(0);
472     }
473     return date;
474 }
475
476 /*******************************
477  * Compute which day of the week a d_time t is.
478  * Returns:
479  *  Integer in the range 0..6, where 0 represents Sunday
480  *  and 6 represents Saturday.
481  */
482 int WeekDay(d_time t)
483 {   int w;
484
485     w = (cast(int)Day(t) + 4) % 7;
486     if (w < 0)
487     w += 7;
488     return w;
489 }
490
491 /***********************************
492  * Convert from UTC to local time.
493  */
494
495 d_time UTCtoLocalTime(d_time t)
496 {
497     return t + LocalTZA + DaylightSavingTA(t);
498 }
499
500 /***********************************
501  * Convert from local time to UTC.
502  */
503
504 d_time LocalTimetoUTC(d_time t)
505 {
506     return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
507 }
508
509
510 d_time MakeTime(d_time hour, d_time min, d_time sec, d_time ms)
511 {
512     return hour * TicksPerHour +
513        min * TicksPerMinute +
514        sec * TicksPerSecond +
515        ms * TicksPerMs;
516 }
517
518
519 d_time MakeDay(d_time year, d_time month, d_time date)
520 {   d_time t;
521     int y;
522     int m;
523     int leap;
524
525     y = cast(int)(year + floor(month, 12));
526     m = dmod(month, 12);
527
528     leap = LeapYear(y);
529     t = TimeFromYear(y) + cast(d_time)mdays[m] * msPerDay;
530     if (leap && month >= 2)
531     t += msPerDay;
532
533     if (YearFromTime(t) != y ||
534     MonthFromTime(t) != m ||
535     DateFromTime(t) != 1)
536     {
537     return  d_time_nan;
538     }
539
540     return Day(t) + date - 1;
541 }
542
543 d_time MakeDate(d_time day, d_time time)
544 {
545     if (day == d_time_nan || time == d_time_nan)
546     return d_time_nan;
547
548     return day * TicksPerDay + time;
549 }
550
551 d_time TimeClip(d_time time)
552 {
553     //printf("TimeClip(%g) = %g\n", time, toInteger(time));
554
555     return toInteger(time);
556 }
557
558 /*************************************
559  * Converts UTC time into a text string of the form:
560  * "Www Mmm dd hh:mm:ss GMT+-TZ yyyy".
561  * For example, "Tue Apr 02 02:04:57 GMT-0800 1996".
562  * If time is invalid, i.e. is d_time_nan,
563  * the string "Invalid date" is returned.
564  *
565  * Example:
566  * ------------------------------------
567   d_time lNow;
568   char[] lNowString;
569
570   // Grab the date and time relative to UTC
571   lNow = std.date.getUTCtime();
572   // Convert this into the local date and time for display.
573   lNowString = std.date.toString(lNow);
574  * ------------------------------------
575  */
576
577 char[] toString(d_time time)
578 {
579     d_time t;
580     char sign;
581     int hr;
582     int mn;
583     int len;
584     d_time offset;
585     d_time dst;
586
587     // Years are supposed to be -285616 .. 285616, or 7 digits
588     // "Tue Apr 02 02:04:57 GMT-0800 1996"
589     char[] buffer = new char[29 + 7 + 1];
590
591     if (time == d_time_nan)
592     return "Invalid Date";
593
594     dst = DaylightSavingTA(time);
595     offset = LocalTZA + dst;
596     t = time + offset;
597     sign = '+';
598     if (offset < 0)
599     {   sign = '-';
600 //  offset = -offset;
601     offset = -(LocalTZA + dst);
602     }
603
604     mn = cast(int)(offset / msPerMinute);
605     hr = mn / 60;
606     mn %= 60;
607
608     //printf("hr = %d, offset = %g, LocalTZA = %g, dst = %g, + = %g\n", hr, offset, LocalTZA, dst, LocalTZA + dst);
609
610     len = sprintf(buffer.ptr, "%.3s %.3s %02d %02d:%02d:%02d GMT%c%02d%02d %d",
611     &daystr[WeekDay(t) * 3],
612     &monstr[MonthFromTime(t) * 3],
613     DateFromTime(t),
614     HourFromTime(t), MinFromTime(t), SecFromTime(t),
615     sign, hr, mn,
616     cast(long)YearFromTime(t));
617
618     // Ensure no buggy buffer overflows
619     //printf("len = %d, buffer.length = %d\n", len, buffer.length);
620     assert(len < buffer.length);
621
622     return buffer[0 .. len];
623 }
624
625 /***********************************
626  * Converts t into a text string of the form: "Www, dd Mmm yyyy hh:mm:ss UTC".
627  * If t is invalid, "Invalid date" is returned.
628  */
629
630 char[] toUTCString(d_time t)
631 {
632     // Years are supposed to be -285616 .. 285616, or 7 digits
633     // "Tue, 02 Apr 1996 02:04:57 GMT"
634     char[] buffer = new char[25 + 7 + 1];
635     int len;
636
637     if (t == d_time_nan)
638     return "Invalid Date";
639
640     len = sprintf(buffer.ptr, "%.3s, %02d %.3s %d %02d:%02d:%02d UTC",
641     &daystr[WeekDay(t) * 3], DateFromTime(t),
642     &monstr[MonthFromTime(t) * 3],
643     YearFromTime(t),
644     HourFromTime(t), MinFromTime(t), SecFromTime(t));
645
646     // Ensure no buggy buffer overflows
647     assert(len < buffer.length);
648
649     return buffer[0 .. len];
650 }
651
652 /************************************
653  * Converts the date portion of time into a text string of the form: "Www Mmm dd
654  * yyyy", for example, "Tue Apr 02 1996".
655  * If time is invalid, "Invalid date" is returned.
656  */
657
658 char[] toDateString(d_time time)
659 {
660     d_time t;
661     d_time offset;
662     d_time dst;
663     int len;
664
665     // Years are supposed to be -285616 .. 285616, or 7 digits
666     // "Tue Apr 02 1996"
667     char[] buffer = new char[29 + 7 + 1];
668
669     if (time == d_time_nan)
670     return "Invalid Date";
671
672     dst = DaylightSavingTA(time);
673     offset = LocalTZA + dst;
674     t = time + offset;
675
676     len = sprintf(buffer.ptr, "%.3s %.3s %02d %d",
677     &daystr[WeekDay(t) * 3],
678     &monstr[MonthFromTime(t) * 3],
679     DateFromTime(t),
680     cast(long)YearFromTime(t));
681
682     // Ensure no buggy buffer overflows
683     assert(len < buffer.length);
684
685     return buffer[0 .. len];
686 }
687
688 /******************************************
689  * Converts the time portion of t into a text string of the form: "hh:mm:ss
690  * GMT+-TZ", for example, "02:04:57 GMT-0800".
691  * If t is invalid, "Invalid date" is returned.
692  */
693
694 char[] toTimeString(d_time time)
695 {
696     d_time t;
697     char sign;
698 &nb