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