1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2005 John Chapman. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Mid 2005: Initial release
8                         Apr 2007: reshaped                        
9 
10         author:         John Chapman, Kris
11 
12 ******************************************************************************/
13 
14 module tango.time.chrono.Calendar;
15 
16 public  import tango.time.Time;
17 
18 private import tango.core.Exception;
19 
20 
21 
22 /**
23  * $(ANCHOR _Calendar)
24  * Represents time in week, month and year divisions.
25  * Remarks: Calendar is the abstract base class for the following Calendar implementations: 
26  *   $(LINK2 #Gregorian, Gregorian), $(LINK2 #Hebrew, Hebrew), $(LINK2 #Hijri, Hijri),
27  *   $(LINK2 #Japanese, Japanese), $(LINK2 #Korean, Korean), $(LINK2 #Taiwan, Taiwan) and
28  *   $(LINK2 #ThaiBuddhist, ThaiBuddhist).
29  */
30 public abstract class Calendar 
31 {
32         //nasty mixin hacks. Clear them and stick with package(tango) when 2.066.1 support ends
33         private template PackageTest()
34         {
35                 mixin(`package(tango) void foo123(){}`);
36         }
37         static if (__traits(compiles, PackageTest!()))
38                 enum packageDecl = q{package(tango)};
39         else
40                 enum packageDecl = q{package};
41         mixin(packageDecl ~ q{{
42         /**
43         * Indicates the current era of the calendar.
44         */
45         enum {CURRENT_ERA = 0};
46 
47         // Corresponds to Win32 calendar IDs
48         enum 
49         {
50                 GREGORIAN = 1,
51                 GREGORIAN_US = 2,
52                 JAPAN = 3,
53                 TAIWAN = 4,
54                 KOREA = 5,
55                 HIJRI = 6,
56                 THAI = 7,
57                 HEBREW = 8,
58                 GREGORIAN_ME_FRENCH = 9,
59                 GREGORIAN_ARABIC = 10,
60                 GREGORIAN_XLIT_ENGLISH = 11,
61                 GREGORIAN_XLIT_FRENCH = 12
62         }
63 
64         enum WeekRule 
65         {
66                 FirstDay,         /// Indicates that the first week of the year is the first week containing the first day of the year.
67                 FirstFullWeek,    /// Indicates that the first week of the year is the first full week following the first day of the year.
68                 FirstFourDayWeek  /// Indicates that the first week of the year is the first week containing at least four days.
69         }
70 
71         enum DatePart
72         {
73                 Year,
74                 Month,
75                 Day,
76                 DayOfYear
77         }
78         }});
79 
80         public enum DayOfWeek 
81         {
82                 Sunday,    /// Indicates _Sunday.
83                 Monday,    /// Indicates _Monday.
84                 Tuesday,   /// Indicates _Tuesday.
85                 Wednesday, /// Indicates _Wednesday.
86                 Thursday,  /// Indicates _Thursday.
87                 Friday,    /// Indicates _Friday.
88                 Saturday   /// Indicates _Saturday.
89         }
90 
91 
92         /**
93          * Get the components of a Time structure using the rules of the
94          * calendar.  This is useful if you want more than one of the given
95          * components.  Note that this doesn't handle the time of day, as that
96          * is calculated directly from the Time struct.
97          *
98          * The default implemenation is to call all the other accessors
99          * directly, a derived class may override if it has a more efficient
100          * method.
101          */
102         const Date toDate (const(Time) time)
103         {
104                 Date d;
105                 split (time, d.year, d.month, d.day, d.doy, d.dow, d.era);
106                 return d;
107         }
108 
109         /**
110          * Get the components of a Time structure using the rules of the
111          * calendar.  This is useful if you want more than one of the given
112          * components.  Note that this doesn't handle the time of day, as that
113          * is calculated directly from the Time struct.
114          *
115          * The default implemenation is to call all the other accessors
116          * directly, a derived class may override if it has a more efficient
117          * method.
118          */
119         const void split (const(Time) time, ref uint year, ref uint month, ref uint day, ref uint doy, ref uint dow, ref uint era)
120         {
121             year = getYear(time);
122             month = getMonth(time);
123             day = getDayOfMonth(time);
124             doy = getDayOfYear(time);
125             dow = getDayOfWeek(time);
126             era = getEra(time);
127         }
128 
129         /**
130         * Returns a Time value set to the specified date and time in the current era.
131         * Params:
132         *   year = An integer representing the _year.
133         *   month = An integer representing the _month.
134         *   day = An integer representing the _day.
135         *   hour = An integer representing the _hour.
136         *   minute = An integer representing the _minute.
137         *   second = An integer representing the _second.
138         *   millisecond = An integer representing the _millisecond.
139         * Returns: The Time set to the specified date and time.
140         */
141         const Time toTime (uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond=0) 
142         {
143                 return toTime (year, month, day, hour, minute, second, millisecond, CURRENT_ERA);
144         }
145 
146         /**
147         * Returns a Time value for the given Date, in the current era 
148         * Params:
149         *   date = a representation of the Date
150         * Returns: The Time set to the specified date.
151         */
152         const Time toTime (const(Date) d) 
153         {
154                 return toTime (d.year, d.month, d.day, 0, 0, 0, 0, d.era);
155         }
156 
157         /**
158         * Returns a Time value for the given DateTime, in the current era 
159         * Params:
160         *   dt = a representation of the date and time
161         * Returns: The Time set to the specified date and time.
162         */
163         const Time toTime (const(DateTime) dt) 
164         {
165                 return toTime (dt.date, dt.time);
166         }
167 
168         /**
169         * Returns a Time value for the given Date and TimeOfDay, in the current era 
170         * Params:
171         *   d = a representation of the date 
172         *   t = a representation of the day time 
173         * Returns: The Time set to the specified date and time.
174         */
175         const Time toTime (const(Date) d, const(TimeOfDay) t) 
176         {
177                 return toTime (d.year, d.month, d.day, t.hours, t.minutes, t.seconds, t.millis, d.era);
178         }
179 
180         /**
181         * When overridden, returns a Time value set to the specified date and time in the specified _era.
182         * Params:
183         *   year = An integer representing the _year.
184         *   month = An integer representing the _month.
185         *   day = An integer representing the _day.
186         *   hour = An integer representing the _hour.
187         *   minute = An integer representing the _minute.
188         *   second = An integer representing the _second.
189         *   millisecond = An integer representing the _millisecond.
190         *   era = An integer representing the _era.
191         * Returns: A Time set to the specified date and time.
192         */
193         abstract const Time toTime (uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond, uint era);
194 
195         /**
196         * When overridden, returns the day of the week in the specified Time.
197         * Params: time = A Time value.
198         * Returns: A DayOfWeek value representing the day of the week of time.
199         */
200         abstract const DayOfWeek getDayOfWeek (const(Time) time);
201 
202         /**
203         * When overridden, returns the day of the month in the specified Time.
204         * Params: time = A Time value.
205         * Returns: An integer representing the day of the month of time.
206         */
207         abstract const uint getDayOfMonth (const(Time) time);
208 
209         /**
210         * When overridden, returns the day of the year in the specified Time.
211         * Params: time = A Time value.
212         * Returns: An integer representing the day of the year of time.
213         */
214         abstract const uint getDayOfYear (const(Time) time);
215 
216         /**
217         * When overridden, returns the month in the specified Time.
218         * Params: time = A Time value.
219         * Returns: An integer representing the month in time.
220         */
221         abstract const uint getMonth (const(Time) time);
222 
223         /**
224         * When overridden, returns the year in the specified Time.
225         * Params: time = A Time value.
226         * Returns: An integer representing the year in time.
227         */
228         abstract const uint getYear (const(Time) time);
229 
230         /**
231         * When overridden, returns the era in the specified Time.
232         * Params: time = A Time value.
233         * Returns: An integer representing the ear in time.
234         */
235         abstract const uint getEra (const(Time) time);
236 
237         /**
238         * Returns the number of days in the specified _year and _month of the current era.
239         * Params:
240         *   year = An integer representing the _year.
241         *   month = An integer representing the _month.
242         * Returns: The number of days in the specified _year and _month of the current era.
243         */
244         const uint getDaysInMonth (uint year, uint month) 
245         {
246                 return getDaysInMonth (year, month, CURRENT_ERA);
247         }
248 
249         /**
250         * When overridden, returns the number of days in the specified _year and _month of the specified _era.
251         * Params:
252         *   year = An integer representing the _year.
253         *   month = An integer representing the _month.
254         *   era = An integer representing the _era.
255         * Returns: The number of days in the specified _year and _month of the specified _era.
256         */
257         abstract const uint getDaysInMonth (uint year, uint month, uint era);
258 
259         /**
260         * Returns the number of days in the specified _year of the current era.
261         * Params: year = An integer representing the _year.
262         * Returns: The number of days in the specified _year in the current era.
263         */
264         const uint getDaysInYear (uint year) 
265         {
266                 return getDaysInYear (year, CURRENT_ERA);
267         }
268 
269         /**
270         * When overridden, returns the number of days in the specified _year of the specified _era.
271         * Params:
272         *   year = An integer representing the _year.
273         *   era = An integer representing the _era.
274         * Returns: The number of days in the specified _year in the specified _era.
275         */
276         abstract const uint getDaysInYear (uint year, uint era);
277 
278         /**
279         * Returns the number of months in the specified _year of the current era.
280         * Params: year = An integer representing the _year.
281         * Returns: The number of months in the specified _year in the current era.
282         */
283         const uint getMonthsInYear (uint year) 
284         {
285                 return getMonthsInYear (year, CURRENT_ERA);
286         }
287 
288         /**
289         * When overridden, returns the number of months in the specified _year of the specified _era.
290         * Params:
291         *   year = An integer representing the _year.
292         *   era = An integer representing the _era.
293         * Returns: The number of months in the specified _year in the specified _era.
294         */
295         abstract const uint getMonthsInYear (uint year, uint era);
296 
297         /**
298         * Returns the week of the year that includes the specified Time.
299         * Params:
300         *   time = A Time value.
301         *   rule = A WeekRule value defining a calendar week.
302         *   firstDayOfWeek = A DayOfWeek value representing the first day of the week.
303         * Returns: An integer representing the week of the year that includes the date in time.
304         */
305         const uint getWeekOfYear (const(Time) time, WeekRule rule, DayOfWeek firstDayOfWeek) 
306         {
307                 auto year = getYear (time);
308                 auto jan1 = cast(int) getDayOfWeek (toTime (year, 1, 1, 0, 0, 0, 0));
309 
310                 switch (rule) 
311                        {
312                        case WeekRule.FirstDay:
313                             int n = jan1 - cast(int) firstDayOfWeek;
314                             if (n < 0)
315                                 n += 7;
316                             return (getDayOfYear (time) + n - 1) / 7 + 1;
317 
318                        case WeekRule.FirstFullWeek:
319                        case WeekRule.FirstFourDayWeek:
320                             int fullDays = (rule is WeekRule.FirstFullWeek) ? 7 : 4;
321                             int n = cast(int) firstDayOfWeek - jan1;
322                             if (n != 0) 
323                                {
324                                if (n < 0)
325                                    n += 7;
326                                else 
327                                   if (n >= fullDays)
328                                       n -= 7;
329                                }
330 
331                             int day = getDayOfYear (time) - n;
332                             if (day > 0)
333                                 return (day - 1) / 7 + 1;
334                             year = getYear(time) - 1;
335                             int month = getMonthsInYear (year);
336                             day = getDaysInMonth (year, month);
337                             return getWeekOfYear(toTime(year, month, day, 0, 0, 0, 0), rule, firstDayOfWeek);
338 
339                        default:
340                             break;
341                        }
342                 throw new IllegalArgumentException("Value was out of range.");
343         }
344 
345         /**
346         * Indicates whether the specified _year in the current era is a leap _year.
347         * Params: year = An integer representing the _year.
348         * Returns: true is the specified _year is a leap _year; otherwise, false.
349         */
350         const bool isLeapYear(uint year) 
351         {
352                 return isLeapYear(year, CURRENT_ERA);
353         }
354 
355         /**
356         * When overridden, indicates whether the specified _year in the specified _era is a leap _year.
357         * Params: year = An integer representing the _year.
358         * Params: era = An integer representing the _era.
359         * Returns: true is the specified _year is a leap _year; otherwise, false.
360         */
361         abstract const bool isLeapYear(uint year, uint era);
362 
363         /**
364         * $(I Property.) When overridden, retrieves the list of eras in the current calendar.
365         * Returns: An integer array representing the eras in the current calendar.
366         */
367         @property abstract const uint[] eras();
368 
369         /**
370         * $(I Property.) Retrieves the identifier associated with the current calendar.
371         * Returns: An integer representing the identifier of the current calendar.
372         */
373         @property const uint id() 
374         {
375                 return -1;
376         }
377 
378         /**
379          * Returns a new Time with the specified number of months added.  If
380          * the months are negative, the months are subtracted.
381          *
382          * If the target month does not support the day component of the input
383          * time, then an error will be thrown, unless truncateDay is set to
384          * true.  If truncateDay is set to true, then the day is reduced to
385          * the maximum day of that month.
386          *
387          * For example, adding one month to 1/31/2000 with truncateDay set to
388          * true results in 2/28/2000.
389          *
390          * The default implementation uses information provided by the
391          * calendar to calculate the correct time to add.  Derived classes may
392          * override if there is a more optimized method.
393          *
394          * Note that the generic method does not take into account crossing
395          * era boundaries.  Derived classes may support this.
396          *
397          * Params: t = A time to add the months to
398          * Params: nMonths = The number of months to add.  This can be
399          * negative.
400          * Params: truncateDay = Round the day down to the maximum day of the
401          * target month if necessary.
402          *
403          * Returns: A Time that represents the provided time with the number
404          * of months added.
405          */
406         const Time addMonths(const(Time) t, int nMonths, bool truncateDay = false)
407         {
408                 uint era = getEra(t);
409                 uint year = getYear(t);
410                 uint month = getMonth(t);
411 
412                 //
413                 // Assume we go back to day 1 of the current year, taking
414                 // into account that offset using the nMonths and nDays
415                 // offsets.
416                 //
417                 nMonths += month - 1;
418                 int origDom = cast(int)getDayOfMonth(t);
419                 long nDays = origDom - cast(int)getDayOfYear(t);
420                 if(nMonths > 0)
421                 {
422                         //
423                         // Adding, add all the years until the year we want to
424                         // be in.
425                         //
426                         auto miy = getMonthsInYear(year, era);
427                         while(nMonths >= miy)
428                         {
429                                 //
430                                 // skip a whole year
431                                 //
432                                 nDays += getDaysInYear(year, era);
433                                 nMonths -= miy;
434                                 year++;
435 
436                                 //
437                                 // update miy
438                                 //
439                                 miy = getMonthsInYear(year, era);
440                         }
441                 }
442                 else if(nMonths < 0)
443                 {
444                         //
445                         // subtracting months
446                         //
447                         while(nMonths < 0)
448                         {
449                                 auto miy = getMonthsInYear(--year, era);
450                                 nDays -= getDaysInYear(year, era);
451                                 nMonths += miy;
452                         }
453                 }
454 
455                 //
456                 // we now are offset to the resulting year.
457                 // Add the rest of the months to get to the day we want.
458                 //
459                 int newDom = cast(int)getDaysInMonth(year, nMonths + 1, era);
460                 if(origDom > newDom)
461                 {
462                     //
463                     // error, the resulting day of month is out of range.  See
464                     // if we should truncate
465                     //
466                     if(truncateDay)
467                         nDays -= newDom - origDom;
468                     else
469                         throw new IllegalArgumentException("days out of range");
470 
471                 }
472                 for(int m = 0; m < nMonths; m++)
473                         nDays += getDaysInMonth(year, m + 1, era);
474                 return t + TimeSpan.fromDays(nDays);
475         }
476 
477         /**
478          * Add the specified number of years to the given Time.
479          *
480          * The generic algorithm uses information provided by the abstract
481          * methods.  Derived classes may re-implement this in order to
482          * optimize the algorithm
483          *
484          * Note that the generic algorithm does not take into account crossing
485          * era boundaries.  Derived classes may support this.
486          *
487          * Params: t = A time to add the years to
488          * Params: nYears = The number of years to add.  This can be negative.
489          *
490          * Returns: A Time that represents the provided time with the number
491          * of years added.
492          */
493         const Time addYears(const(Time) t, int nYears)
494         {
495                 auto date = toDate(t);
496                 auto tod = t.ticks % TimeSpan.TicksPerDay;
497                 if(tod < 0)
498                         tod += TimeSpan.TicksPerDay;
499                 date.year += nYears;
500                 return toTime(date) + TimeSpan(tod);
501         }
502 
503         package static long getTimeTicks (uint hour, uint minute, uint second) 
504         {
505                 return (TimeSpan.fromHours(hour) + TimeSpan.fromMinutes(minute) + TimeSpan.fromSeconds(second)).ticks;
506         }
507 }