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