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