1 /*
  2  * PersRataDie.js - Represent a rata die date in the Persian calendar
  3  *
  4  * Copyright © 2014-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 ilib = require("./ilib.js");
 21 var MathUtils = require("./MathUtils.js");
 22 
 23 var Astro = require("./Astro.js");
 24 var RataDie = require("./RataDie.js");
 25 var GregorianDate = require("./GregorianDate.js");
 26 
 27 /**
 28  * @class
 29  * Construct a new Persian RD date number object. The constructor parameters can
 30  * contain any of the following properties:
 31  *
 32  * <ul>
 33  * <li><i>unixtime<i> - sets the time of this instance according to the given
 34  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
 35  *
 36  * <li><i>julianday</i> - sets the time of this instance according to the given
 37  * Julian Day instance or the Julian Day given as a float
 38  *
 39  * <li><i>year</i> - any integer, including 0
 40  *
 41  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
 42  *
 43  * <li><i>day</i> - 1 to 31
 44  *
 45  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
 46  * is always done with an unambiguous 24 hour representation
 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 Persian 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 Persian RD date
 74  */
 75 var PersRataDie = function(params) {
 76     this.rd = NaN;
 77     Astro.initAstro(
 78         params && typeof(params.sync) === 'boolean' ? params.sync : true,
 79         params && params.loadParams,
 80         ilib.bind(this, function (x) {
 81             RataDie.call(this, params);
 82             if (params && typeof(params.callback) === 'function') {
 83                 params.callback(this);
 84             }
 85         })
 86     );
 87 };
 88 
 89 PersRataDie.prototype = new RataDie();
 90 PersRataDie.prototype.parent = RataDie;
 91 PersRataDie.prototype.constructor = PersRataDie;
 92 
 93 /**
 94  * The difference between a zero Julian day and the first Persian date
 95  * @private
 96  * @type number
 97  */
 98 PersRataDie.prototype.epoch = 1948319.5;
 99 
100 /**
101  * @protected
102  */
103 PersRataDie.prototype._tehranEquinox = function(year) {
104     var equJED, equJD, equAPP, equTehran, dtTehran, eot;
105 
106     //  March equinox in dynamical time
107     equJED = Astro._equinox(year, 0);
108 
109     //  Correct for delta T to obtain Universal time
110     equJD = equJED - (Astro._deltat(year) / (24 * 60 * 60));
111 
112     //  Apply the equation of time to yield the apparent time at Greenwich
113     eot = Astro._equationOfTime(equJED) * 360;
114     eot = (eot - 20 * Math.floor(eot/20)) / 360;
115     equAPP = equJD + eot;
116 
117     /*
118      * Finally, we must correct for the constant difference between
119      * the Greenwich meridian and the time zone standard for Iran
120      * Standard time, 52 degrees 30 minutes to the East.
121      */
122 
123     dtTehran = 52.5 / 360;
124     equTehran = equAPP + dtTehran;
125 
126     return equTehran;
127 };
128 
129 /**
130  * Calculate the year based on the given Julian day.
131  * @protected
132  * @param {number} jd the Julian day to get the year for
133  * @return {{year:number,equinox:number}} the year and the last equinox
134  */
135 PersRataDie.prototype._getYear = function(jd) {
136     var gd = new GregorianDate({julianday: jd});
137     var guess = gd.getYears() - 2,
138         nexteq,
139         ret = {};
140 
141     //ret.equinox = Math.floor(this._tehranEquinox(guess));
142     ret.equinox = this._tehranEquinox(guess);
143     while (ret.equinox > jd) {
144         guess--;
145         // ret.equinox = Math.floor(this._tehranEquinox(guess));
146         ret.equinox = this._tehranEquinox(guess);
147     }
148     nexteq = ret.equinox - 1;
149     // if the equinox falls after noon, then the day after that is the start of the
150     // next year, so truncate the JD to get the noon of the day before the day with
151     //the equinox on it, then add 0.5 to get the midnight of that day
152     while (!(Math.floor(ret.equinox) + 0.5 <= jd && jd < Math.floor(nexteq) + 0.5)) {
153         ret.equinox = nexteq;
154         guess++;
155         // nexteq = Math.floor(this._tehranEquinox(guess));
156         nexteq = this._tehranEquinox(guess);
157     }
158 
159     // Mean solar tropical year is 365.24219878 days
160     ret.year = Math.round((ret.equinox - this.epoch - 1) / 365.24219878) + 1;
161 
162     return ret;
163 };
164 
165 /**
166  * Calculate the Rata Die (fixed day) number of the given date from the
167  * date components.
168  *
169  * @protected
170  * @param {Object} date the date components to calculate the RD from
171  */
172 PersRataDie.prototype._setDateComponents = function(date) {
173     var adr, guess, jd;
174 
175     // Mean solar tropical year is 365.24219878 days
176     guess = this.epoch + 1 + 365.24219878 * ((date.year || 0) - 2);
177     adr = {year: (date.year || 0) - 1, equinox: 0};
178 
179     while (adr.year < date.year) {
180         adr = this._getYear(guess);
181         guess = adr.equinox + (365.24219878 + 2);
182     }
183 
184     jd = Math.floor(adr.equinox) +
185             (((date.month || 0) <= 7) ?
186                 (((date.month || 1) - 1) * 31) :
187                 ((((date.month || 1) - 1) * 30) + 6)
188             ) +
189             ((date.day || 1) - 1 + 0.5); // add 0.5 so that we convert JDs, which start at noon to RDs which start at midnight
190 
191     jd += ((date.hour || 0) * 3600000 +
192             (date.minute || 0) * 60000 +
193             (date.second || 0) * 1000 +
194             (date.millisecond || 0)) /
195             86400000;
196 
197     this.rd = jd - this.epoch;
198 };
199 
200 /**
201  * Return the rd number of the particular day of the week on or before the
202  * given rd. eg. The Sunday on or before the given rd.
203  * @private
204  * @param {number} rd the rata die date of the reference date
205  * @param {number} dayOfWeek the day of the week that is being sought relative
206  * to the current date
207  * @return {number} the rd of the day of the week
208  */
209 PersRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
210     return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 3, 7);
211 };
212 
213 module.exports = PersRataDie;