1 /*
  2  * HanDate.js - Represent a date in the Han algorithmic calendar
  3  *
  4  * Copyright © 2014-2015, 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 ilib = require("../index.js");
 21 var MathUtils = require("./MathUtils.js");
 22 var HanCal = require("./HanCal.js");
 23 var RataDie = require("./RataDie.js");
 24 
 25 /**
 26  * Construct a new Han 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, Gregorian
 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>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
 37  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious
 38  * linear count of years since the beginning of the epoch, much like other calendars. This linear
 39  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0
 40  * to 60 and treated as if it were a year in the regular 60-year cycle.
 41  *
 42  * <li><i>year</i> - any integer, including 0
 43  *
 44  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
 45  *
 46  * <li><i>day</i> - 1 to 31
 47  *
 48  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
 49  * is always done with an unambiguous 24 hour representation
 50  *
 51  * <li><i>minute</i> - 0 to 59
 52  *
 53  * <li><i>second</i> - 0 to 59
 54  *
 55  * <li><i>millisecond</i> - 0 to 999
 56  *
 57  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
 58  * </ul>
 59  *
 60  * If the constructor is called with another Han date instance instead of
 61  * a parameter block, the other instance acts as a parameter block and its
 62  * settings are copied into the current instance.<p>
 63  *
 64  * If the constructor is called with no arguments at all or if none of the
 65  * properties listed above are present, then the RD is calculate based on
 66  * the current date at the time of instantiation. <p>
 67  *
 68  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
 69  * specified in the params, it is assumed that they have the smallest possible
 70  * value in the range for the property (zero or one).<p>
 71  *
 72  *
 73  * @private
 74  * @class
 75  * @constructor
 76  * @extends RataDie
 77  * @param {Object=} params parameters that govern the settings and behaviour of this Han RD date
 78  */
 79 var HanRataDie = function(params) {
 80     this.rd = NaN;
 81     if (params && params.cal) {
 82         this.cal = params.cal;
 83         RataDie.call(this, params);
 84         if (params && typeof(params.callback) === 'function') {
 85             params.callback(this);
 86         }
 87     } else {
 88         new HanCal({
 89             sync: params && params.sync,
 90             loadParams: params && params.loadParams,
 91             onLoad: ilib.bind(this, function(c) {
 92                 this.cal = c;
 93                 RataDie.call(this, params);
 94                 if (params && typeof(params.callback) === 'function') {
 95                     params.callback(this);
 96                 }
 97             })
 98         });
 99     }
100 };
101 
102 HanRataDie.prototype = new RataDie();
103 HanRataDie.prototype.parent = RataDie;
104 HanRataDie.prototype.constructor = HanRataDie;
105 
106 /**
107  * The difference between a zero Julian day and the first Han date
108  * which is February 15, -2636 (Gregorian).
109  * @private
110  * @type number
111  */
112 HanRataDie.epoch = 758325.5;
113 
114 /**
115  * Calculate the Rata Die (fixed day) number of the given date from the
116  * date components.
117  *
118  * @protected
119  * @param {Object} date the date components to calculate the RD from
120  */
121 HanRataDie.prototype._setDateComponents = function(date) {
122     var calc = HanCal._leapYearCalc(date.year, date.cycle);
123     var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
124     var newYears;
125     this.leapYear = (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12);
126     if (this.leapYear && (HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2)) ) {
127         newYears = HanCal._newMoonOnOrAfter(m2+1);
128     } else {
129         newYears = m2;
130     }
131 
132     var priorNewMoon = HanCal._newMoonOnOrAfter(calc.m1 + date.month * 29); // this is a julian day
133     this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(priorNewMoon));
134     this.leapMonth = (this.leapYear && HanCal._noMajorST(priorNewMoon) && !this.priorLeapMonth);
135 
136     var rdtime = (date.hour * 3600000 +
137         date.minute * 60000 +
138         date.second * 1000 +
139         date.millisecond) /
140         86400000;
141 
142     /*
143     console.log("getRataDie: converting " +  JSON.stringify(date) + " to an RD");
144     console.log("getRataDie: year is " +  date.year + " plus cycle " + date.cycle);
145     console.log("getRataDie: isLeapYear is " +  this.leapYear);
146     console.log("getRataDie: priorNewMoon is " +  priorNewMoon);
147     console.log("getRataDie: day in month is " +  date.day);
148     console.log("getRataDie: rdtime is " +  rdtime);
149     console.log("getRataDie: rd is " +  (priorNewMoon + date.day - 1 + rdtime));
150     */
151 
152     this.rd = priorNewMoon + date.day - 1 + rdtime - RataDie.gregorianEpoch;
153 };
154 
155 /**
156  * Return the rd number of the particular day of the week on or before the
157  * given rd. eg. The Sunday on or before the given rd.
158  * @private
159  * @param {number} rd the rata die date of the reference date
160  * @param {number} dayOfWeek the day of the week that is being sought relative
161  * to the current date
162  * @return {number} the rd of the day of the week
163  */
164 HanRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
165     return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek, 7);
166 };
167 
168 /**
169  * @protected
170  * @static
171  * @param {number} jd1 first julian day
172  * @param {number} jd2 second julian day
173  * @returns {boolean} true if there is a leap month earlier in the same year
174  * as the given months
175  */
176 HanRataDie._priorLeapMonth = function(jd1, jd2) {
177     return jd2 >= jd1 &&
178         (HanRataDie._priorLeapMonth(jd1, HanCal._newMoonBefore(jd2)) ||
179                 HanCal._noMajorST(jd2));
180 };
181 
182 
183 module.exports = HanRataDie;