1 /*
  2  * HebrewRataDie.js - Represent an RD date in the Hebrew calendar
  3  *
  4  * Copyright © 2012-2015, 2018, JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  *     http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 var HebrewCal = require("./HebrewCal.js");
 21 var MathUtils = require("./MathUtils.js");
 22 var RataDie = require("./RataDie.js");
 23 
 24 /**
 25  * @class
 26  * Construct a new Hebrew RD date number object. The constructor parameters can
 27  * contain any of the following properties:
 28  *
 29  * <ul>
 30  * <li><i>unixtime<i> - sets the time of this instance according to the given
 31  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
 32  *
 33  * <li><i>julianday</i> - sets the time of this instance according to the given
 34  * Julian Day instance or the Julian Day given as a float
 35  *
 36  * <li><i>year</i> - any integer, including 0
 37  *
 38  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
 39  *
 40  * <li><i>day</i> - 1 to 31
 41  *
 42  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
 43  * is always done with an unambiguous 24 hour representation
 44  *
 45  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify
 46  * the parts or specify the minutes, seconds, and milliseconds, but not both.
 47  *
 48  * <li><i>minute</i> - 0 to 59
 49  *
 50  * <li><i>second</i> - 0 to 59
 51  *
 52  * <li><i>millisecond</i> - 0 to 999
 53  *
 54  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
 55  * </ul>
 56  *
 57  * If the constructor is called with another Hebrew date instance instead of
 58  * a parameter block, the other instance acts as a parameter block and its
 59  * settings are copied into the current instance.<p>
 60  *
 61  * If the constructor is called with no arguments at all or if none of the
 62  * properties listed above are present, then the RD is calculate based on
 63  * the current date at the time of instantiation. <p>
 64  *
 65  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
 66  * specified in the params, it is assumed that they have the smallest possible
 67  * value in the range for the property (zero or one).<p>
 68  *
 69  *
 70  * @private
 71  * @constructor
 72  * @extends RataDie
 73  * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew RD date
 74  */
 75 var HebrewRataDie = function(params) {
 76     this.cal = params && params.cal || new HebrewCal();
 77     this.rd = NaN;
 78     RataDie.call(this, params);
 79 };
 80 
 81 HebrewRataDie.prototype = new RataDie();
 82 HebrewRataDie.prototype.parent = RataDie;
 83 HebrewRataDie.prototype.constructor = HebrewRataDie;
 84 
 85 /**
 86  * The difference between a zero Julian day and the first day of the Hebrew
 87  * calendar: sunset on Monday, Tishri 1, 1 = September 7, 3760 BC Gregorian = JD 347997.25
 88  * @private
 89  * @type number
 90  */
 91 HebrewRataDie.prototype.epoch = 347997.25;
 92 
 93 /**
 94  * the cumulative lengths of each month for a non-leap year, without new years corrections
 95  * @private
 96  * @const
 97  * @type Array.<number>
 98  */
 99 HebrewRataDie.cumMonthLengths = [
100     176,  /* Nisan */
101     206,  /* Iyyar */
102     235,  /* Sivan */
103     265,  /* Tammuz */
104     294,  /* Av */
105     324,  /* Elul */
106     0,    /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
107     30,   /* Heshvan */
108     59,   /* Kislev */
109     88,   /* Teveth */
110     117,  /* Shevat */
111     147   /* Adar I */
112 ];
113 
114 /**
115  * the cumulative lengths of each month for a leap year, without new years corrections
116  * @private
117  * @const
118  * @type Array.<number>
119  */
120 HebrewRataDie.cumMonthLengthsLeap = [
121     206,  /* Nisan */
122     236,  /* Iyyar */
123     265,  /* Sivan */
124     295,  /* Tammuz */
125     324,  /* Av */
126     354,  /* Elul */
127     0,    /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
128     30,   /* Heshvan */
129     59,   /* Kislev */
130     88,   /* Teveth */
131     117,  /* Shevat */
132     147,  /* Adar I */
133     177   /* Adar II */
134 ];
135 
136 /**
137  * Calculate the Rata Die (fixed day) number of the given date from the
138  * date components.
139  *
140  * @private
141  * @param {Object} date the date components to calculate the RD from
142  */
143 HebrewRataDie.prototype._setDateComponents = function(date) {
144     var elapsed = HebrewCal.elapsedDays(date.year);
145     var days = elapsed +
146         HebrewCal.newYearsCorrection(date.year, elapsed) +
147         date.day - 1;
148     var sum = 0, table;
149 
150     //console.log("getRataDie: converting " +  JSON.stringify(date));
151     //console.log("getRataDie: days is " +  days);
152     //console.log("getRataDie: new years correction is " +  HebrewCal.newYearsCorrection(date.year, elapsed));
153 
154     table = this.cal.isLeapYear(date.year) ?
155         HebrewRataDie.cumMonthLengthsLeap :
156         HebrewRataDie.cumMonthLengths;
157     sum = table[date.month-1];
158 
159     // gets cumulative without correction, so now add in the correction
160     if ((date.month < 7 || date.month > 8) && HebrewCal.longHeshvan(date.year)) {
161         sum++;
162     }
163     if ((date.month < 7 || date.month > 9) && HebrewCal.longKislev(date.year)) {
164         sum++;
165     }
166     // console.log("getRataDie: cum days is now " +  sum);
167 
168     days += sum;
169 
170     // the date starts at sunset, which we take as 18:00, so the hours from
171     // midnight to 18:00 are on the current Gregorian day, and the hours from
172     // 18:00 to midnight are on the previous Gregorian day. So to calculate the
173     // number of hours into the current day that this time represents, we have
174     // to count from 18:00 to midnight first, and add in 6 hours if the time is
175     // less than 18:00
176     var minute, second, millisecond;
177 
178     if (typeof(date.parts) !== 'undefined') {
179         // The parts (halaqim) of the hour. This can be a number from 0 to 1079.
180         var parts = parseInt(date.parts, 10);
181         var seconds = parseInt(parts, 10) * 3.333333333333;
182         minute = Math.floor(seconds / 60);
183         seconds -= minute * 60;
184         second = Math.floor(seconds);
185         millisecond = (seconds - second);
186     } else {
187         minute = parseInt(date.minute, 10) || 0;
188         second = parseInt(date.second, 10) || 0;
189         millisecond = parseInt(date.millisecond, 10) || 0;
190     }
191 
192     var time;
193     if (date.hour >= 18) {
194         time = ((date.hour - 18 || 0) * 3600000 +
195             (minute || 0) * 60000 +
196             (second || 0) * 1000 +
197             (millisecond || 0)) /
198             86400000;
199     } else {
200         time = 0.25 +    // 6 hours from 18:00 to midnight on the previous gregorian day
201                 ((date.hour || 0) * 3600000 +
202                 (minute || 0) * 60000 +
203                 (second || 0) * 1000 +
204                 (millisecond || 0)) /
205                 86400000;
206     }
207 
208     //console.log("getRataDie: rd is " +  (days + time));
209     this.rd = days + time;
210 };
211 
212 /**
213  * Return the rd number of the particular day of the week on or before the
214  * given rd. eg. The Sunday on or before the given rd.
215  * @private
216  * @param {number} rd the rata die date of the reference date
217  * @param {number} dayOfWeek the day of the week that is being sought relative
218  * to the current date
219  * @return {number} the rd of the day of the week
220  */
221 HebrewRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
222     return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek + 1, 7);
223 };
224 
225 module.exports = HebrewRataDie;
226