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, snoyberg
17 
18 ******************************************************************************/
19 
20 module ocean.time.chrono.Hebrew;
21 
22 import ocean.core.ExceptionDefinitions;
23 
24 import ocean.time.chrono.Calendar;
25 
26 
27 
28 /**
29  * $(ANCHOR _Hebrew)
30  * Represents the Hebrew calendar.
31  */
32 public class Hebrew : Calendar {
33 
34   private static immutable uint[14][7] MonthDays = [
35     // month                                                    // year type
36     [ 0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0  ],
37     [ 0, 30, 29, 29, 29, 30, 29, 0,  30, 29, 30, 29, 30, 29 ],  // 1
38     [ 0, 30, 29, 30, 29, 30, 29, 0,  30, 29, 30, 29, 30, 29 ],  // 2
39     [ 0, 30, 30, 30, 29, 30, 29, 0,  30, 29, 30, 29, 30, 29 ],  // 3
40     [ 0, 30, 29, 29, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29 ],  // 4
41     [ 0, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29 ],  // 5
42     [ 0, 30, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29 ]   // 6
43   ];
44 
45   private static immutable uint YearOfOneAD = 3760;
46   private static immutable uint DaysToOneAD = cast(int)(YearOfOneAD * 365.2735);
47 
48   private static immutable uint PartsPerHour = 1080;
49   private static immutable uint PartsPerDay = 24 * PartsPerHour;
50   private static immutable uint DaysPerMonth = 29;
51   private static immutable uint DaysPerMonthFraction = 12 * PartsPerHour + 793;
52   private static immutable uint PartsPerMonth = DaysPerMonth * PartsPerDay + DaysPerMonthFraction;
53   private static immutable uint FirstNewMoon = 11 * PartsPerHour + 204;
54 
55   private uint minYear_ = YearOfOneAD + 1583;
56   private uint maxYear_ = YearOfOneAD + 2240;
57 
58   /**
59    * Represents the current era.
60    */
61   public static immutable uint HEBREW_ERA = 1;
62 
63   /**
64    * Overridden. Returns a Time value set to the specified date and time in the specified _era.
65    * Params:
66    *   year = An integer representing the _year.
67    *   month = An integer representing the _month.
68    *   day = An integer representing the _day.
69    *   hour = An integer representing the _hour.
70    *   minute = An integer representing the _minute.
71    *   second = An integer representing the _second.
72    *   millisecond = An integer representing the _millisecond.
73    *   era = An integer representing the _era.
74    * Returns: A Time set to the specified date and time.
75    */
76   public override Time toTime(uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond, uint era) {
77     checkYear(year, era);
78     return getGregorianTime(year, month, day, hour, minute, second, millisecond);
79   }
80 
81   /**
82    * Overridden. Returns the day of the week in the specified Time.
83    * Params: time = A Time value.
84    * Returns: A DayOfWeek value representing the day of the week of time.
85    */
86   public override DayOfWeek getDayOfWeek(Time time) {
87     return cast(DayOfWeek) cast(uint) ((time.ticks / TimeSpan.TicksPerDay + 1) % 7);
88   }
89 
90   /**
91    * Overridden. Returns the day of the month in the specified Time.
92    * Params: time = A Time value.
93    * Returns: An integer representing the day of the month of time.
94    */
95   public override uint getDayOfMonth(Time time) {
96     auto year = getYear(time);
97     auto yearType = getYearType(year);
98     auto days = getStartOfYear(year) - DaysToOneAD;
99     auto day = cast(int)(time.ticks / TimeSpan.TicksPerDay) - days;
100     uint n;
101     while (n < 12 && day >= MonthDays[yearType][n + 1]) {
102       day -= MonthDays[yearType][n + 1];
103       n++;
104     }
105     return day + 1;
106   }
107 
108   /**
109    * Overridden. Returns the day of the year in the specified Time.
110    * Params: time = A Time value.
111    * Returns: An integer representing the day of the year of time.
112    */
113   public override uint getDayOfYear(Time time) {
114     auto year = getYear(time);
115     auto days = getStartOfYear(year) - DaysToOneAD;
116     return (cast(uint)(time.ticks / TimeSpan.TicksPerDay) - days) + 1;
117   }
118 
119   /**
120    * Overridden. Returns the month in the specified Time.
121    * Params: time = A Time value.
122    * Returns: An integer representing the month in time.
123    */
124   public override uint getMonth(Time time) {
125     auto year = getYear(time);
126     auto yearType = getYearType(year);
127     auto days = getStartOfYear(year) - DaysToOneAD;
128     auto day = cast(int)(time.ticks / TimeSpan.TicksPerDay) - days;
129     uint n;
130     while (n < 12 && day >= MonthDays[yearType][n + 1]) {
131       day -= MonthDays[yearType][n + 1];
132       n++;
133     }
134     return n + 1;
135   }
136 
137   /**
138    * Overridden. Returns the year in the specified Time.
139    * Params: time = A Time value.
140    * Returns: An integer representing the year in time.
141    */
142   public override uint getYear(Time time) {
143     auto day = cast(uint)(time.ticks / TimeSpan.TicksPerDay) + DaysToOneAD;
144     auto low = minYear_, high = maxYear_;
145     // Perform a binary search.
146     while (low <= high) {
147       auto mid = low + (high - low) / 2;
148       auto startDay = getStartOfYear(mid);
149       if (day < startDay)
150         high = mid - 1;
151       else if (day >= startDay && day < getStartOfYear(mid + 1))
152         return mid;
153       else
154         low = mid + 1;
155     }
156     return low;
157   }
158 
159   /**
160    * Overridden. Returns the era in the specified Time.
161    * Params: time = A Time value.
162    * Returns: An integer representing the ear in time.
163    */
164   public override uint getEra(Time time) {
165     return HEBREW_ERA;
166   }
167 
168   /**
169    * Overridden. Returns the number of days in the specified _year and _month of the specified _era.
170    * Params:
171    *   year = An integer representing the _year.
172    *   month = An integer representing the _month.
173    *   era = An integer representing the _era.
174    * Returns: The number of days in the specified _year and _month of the specified _era.
175    */
176   public override uint getDaysInMonth(uint year, uint month, uint era) {
177     checkYear(year, era);
178     return MonthDays[getYearType(year)][month];
179   }
180 
181   /**
182    * Overridden. Returns the number of days in the specified _year of the specified _era.
183    * Params:
184    *   year = An integer representing the _year.
185    *   era = An integer representing the _era.
186    * Returns: The number of days in the specified _year in the specified _era.
187    */
188   public override uint getDaysInYear(uint year, uint era) {
189     return getStartOfYear(year + 1) - getStartOfYear(year);
190   }
191 
192   /**
193    * Overridden. Returns the number of months in the specified _year of the specified _era.
194    * Params:
195    *   year = An integer representing the _year.
196    *   era = An integer representing the _era.
197    * Returns: The number of months in the specified _year in the specified _era.
198    */
199   public override uint getMonthsInYear(uint year, uint era) {
200     return isLeapYear(year, era) ? 13 : 12;
201   }
202 
203   /**
204    * Overridden. Indicates whether the specified _year in the specified _era is a leap _year.
205    * Params: year = An integer representing the _year.
206    *         era = An integer representing the _era.
207    * Returns: true is the specified _year is a leap _year; otherwise, false.
208    */
209   public override bool isLeapYear(uint year, uint era) {
210     checkYear(year, era);
211     // true if year % 19 == 0, 3, 6, 8, 11, 14, 17
212     return ((7 * year + 1) % 19) < 7;
213   }
214 
215   /**
216    * $(I Property.) Overridden. Retrieves the list of eras in the current calendar.
217    * Returns: An integer array representing the eras in the current calendar.
218    */
219   public override uint[] eras() {
220         auto tmp = [HEBREW_ERA];
221         return tmp.dup;
222   }
223 
224   /**
225    * $(I Property.) Overridden. Retrieves the identifier associated with the current calendar.
226    * Returns: An integer representing the identifier of the current calendar.
227    */
228   public override uint id() {
229     return HEBREW;
230   }
231 
232   private void checkYear(uint year, uint era) {
233     if ((era != CURRENT_ERA && era != HEBREW_ERA) || (year > maxYear_ || year < minYear_))
234       throw new IllegalArgumentException("Value was out of range.");
235   }
236 
237   private uint getYearType(uint year) {
238     int yearLength = getStartOfYear(year + 1) - getStartOfYear(year);
239     if (yearLength > 380)
240       yearLength -= 30;
241     switch (yearLength) {
242       case 353:
243         // "deficient"
244         return 1;
245       case 383:
246         // "deficient" leap
247         return 4;
248       case 354:
249         // "normal"
250         return 2;
251       case 384:
252         // "normal" leap
253         return 5;
254       case 355:
255         // "complete"
256         return 3;
257       case 385:
258         // "complete" leap
259         return 6;
260       default:
261         break;
262     }
263     // Satisfies -w
264     throw new IllegalArgumentException("Value was not valid.");
265   }
266 
267   private uint getStartOfYear(uint year) {
268     auto months = (235 * year - 234) / 19;
269     auto fraction = months * DaysPerMonthFraction + FirstNewMoon;
270     auto day = months * 29 + (fraction / PartsPerDay);
271     fraction %= PartsPerDay;
272 
273     auto dayOfWeek = day % 7;
274     if (dayOfWeek == 2 || dayOfWeek == 4 || dayOfWeek == 6) {
275       day++;
276       dayOfWeek = day % 7;
277     }
278     if (dayOfWeek == 1 && fraction > 15 * PartsPerHour + 204 && !isLeapYear(year, CURRENT_ERA))
279       day += 2;
280     else if (dayOfWeek == 0 && fraction > 21 * PartsPerHour + 589 && isLeapYear(year, CURRENT_ERA))
281       day++;
282     return day;
283   }
284 
285   private Time getGregorianTime(uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond) {
286     auto yearType = getYearType(year);
287     auto days = getStartOfYear(year) - DaysToOneAD + day - 1;
288     for (int i = 1; i <= month; i++)
289       days += MonthDays[yearType][i - 1];
290     return Time((days * TimeSpan.TicksPerDay) + getTimeTicks(hour, minute, second)) + TimeSpan.fromMillis(millisecond);
291   }
292 
293 }
294