1 /* 2 * HebrewCal.js - Represent a Hebrew calendar object. 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 MathUtils = require("./MathUtils.js"); 21 var Calendar = require("./Calendar.js"); 22 23 /** 24 * @class 25 * Construct a new Hebrew calendar object. This class encodes information about 26 * the Hebrew (Jewish) calendar. The Hebrew calendar is a tabular hebrew 27 * calendar where the dates are calculated by arithmetic rules. This differs from 28 * the religious Hebrew calendar which is used to mark the beginning of particular 29 * holidays. The religious calendar depends on the first sighting of the new 30 * crescent moon to determine the first day of the new month. Because humans and 31 * weather are both involved, the actual time of sighting varies, so it is not 32 * really possible to precalculate the religious calendar. Certain groups, such 33 * as the Hebrew Society of North America, decreed in in 2007 that they will use 34 * a calendar based on calculations rather than observations to determine the 35 * beginning of lunar months, and therefore the dates of holidays.<p> 36 * 37 * @param {Object=} options Options governing the construction of this instance 38 * @constructor 39 * @extends Calendar 40 */ 41 var HebrewCal = function(options) { 42 this.type = "hebrew"; 43 44 if (options && typeof(options.onLoad) === "function") { 45 options.onLoad(this); 46 } 47 }; 48 49 /** 50 * Return the number of days elapsed in the Hebrew calendar before the 51 * given year starts. 52 * @private 53 * @param {number} year the year for which the number of days is sought 54 * @return {number} the number of days elapsed in the Hebrew calendar before the 55 * given year starts 56 */ 57 HebrewCal.elapsedDays = function(year) { 58 var months = Math.floor(((235*year) - 234)/19); 59 var parts = 204 + 793 * MathUtils.mod(months, 1080); 60 var hours = 11 + 12 * months + 793 * Math.floor(months/1080) + 61 Math.floor(parts/1080); 62 var days = 29 * months + Math.floor(hours/24); 63 return (MathUtils.mod(3 * (days + 1), 7) < 3) ? days + 1 : days; 64 }; 65 66 /** 67 * Return the number of days that the New Year's (Rosh HaShanah) in the Hebrew 68 * calendar will be corrected for the given year. Corrections are caused because New 69 * Year's is not allowed to start on certain days of the week. To deal with 70 * it, the start of the new year is corrected for the next year by adding a 71 * day to the 8th month (Heshvan) and/or the 9th month (Kislev) in the current 72 * year to make them 30 days long instead of 29. 73 * 74 * @private 75 * @param {number} year the year for which the correction is sought 76 * @param {number} elapsed number of days elapsed up to this year 77 * @return {number} the number of days correction in the current year to make sure 78 * Rosh HaShanah does not fall on undesirable days of the week 79 */ 80 HebrewCal.newYearsCorrection = function(year, elapsed) { 81 var lastYear = HebrewCal.elapsedDays(year-1), 82 thisYear = elapsed, 83 nextYear = HebrewCal.elapsedDays(year+1); 84 85 return (nextYear - thisYear) == 356 ? 2 : ((thisYear - lastYear) == 382 ? 1 : 0); 86 }; 87 88 /** 89 * Return the rata die date of the new year for the given hebrew year. 90 * @private 91 * @param {number} year the year for which the new year is needed 92 * @return {number} the rata die date of the new year 93 */ 94 HebrewCal.newYear = function(year) { 95 var elapsed = HebrewCal.elapsedDays(year); 96 97 return elapsed + HebrewCal.newYearsCorrection(year, elapsed); 98 }; 99 100 /** 101 * Return the number of days in the given year. Years contain a variable number of 102 * days because the date of Rosh HaShanah (New Year's) changes so that it doesn't 103 * fall on particular days of the week. Days are added to the months of Heshvan 104 * and/or Kislev in the previous year in order to prevent the current year's New 105 * Year from being on Sunday, Wednesday, or Friday. 106 * 107 * @param {number} year the year for which the length is sought 108 * @return {number} number of days in the given year 109 */ 110 HebrewCal.daysInYear = function(year) { 111 return HebrewCal.newYear(year+1) - HebrewCal.newYear(year); 112 }; 113 114 /** 115 * Return true if the given year contains a long month of Heshvan. That is, 116 * it is 30 days instead of 29. 117 * 118 * @private 119 * @param {number} year the year in which that month is questioned 120 * @return {boolean} true if the given year contains a long month of Heshvan 121 */ 122 HebrewCal.longHeshvan = function(year) { 123 return MathUtils.mod(HebrewCal.daysInYear(year), 10) === 5; 124 }; 125 126 /** 127 * Return true if the given year contains a long month of Kislev. That is, 128 * it is 30 days instead of 29. 129 * 130 * @private 131 * @param {number} year the year in which that month is questioned 132 * @return {boolean} true if the given year contains a short month of Kislev 133 */ 134 HebrewCal.longKislev = function(year) { 135 return MathUtils.mod(HebrewCal.daysInYear(year), 10) !== 3; 136 }; 137 138 /** 139 * Return the date of the last day of the month for the given year. The date of 140 * the last day of the month is variable because a number of months gain an extra 141 * day in leap years, and it is variable which months gain a day for each leap 142 * year and which do not. 143 * 144 * @param {number} month the month for which the number of days is sought 145 * @param {number} year the year in which that month is 146 * @return {number} the number of days in the given month and year 147 */ 148 HebrewCal.prototype.lastDayOfMonth = function(month, year) { 149 switch (month) { 150 case 2: 151 case 4: 152 case 6: 153 case 10: 154 return 29; 155 case 13: 156 return this.isLeapYear(year) ? 29 : 0; 157 case 8: 158 return HebrewCal.longHeshvan(year) ? 30 : 29; 159 case 9: 160 return HebrewCal.longKislev(year) ? 30 : 29; 161 case 12: 162 case 1: 163 case 3: 164 case 5: 165 case 7: 166 case 11: 167 return 30; 168 default: 169 return 0; 170 } 171 }; 172 173 /** 174 * Return the number of months in the given year. The number of months in a year varies 175 * for luni-solar calendars because in some years, an extra month is needed to extend the 176 * days in a year to an entire solar year. The month is represented as a 1-based number 177 * where 1=first month, 2=second month, etc. 178 * 179 * @param {number} year a year for which the number of months is sought 180 */ 181 HebrewCal.prototype.getNumMonths = function(year) { 182 return this.isLeapYear(year) ? 13 : 12; 183 }; 184 185 /** 186 * Return the number of days in a particular month in a particular year. This function 187 * can return a different number for a month depending on the year because of leap years. 188 * 189 * @param {number} month the month for which the length is sought 190 * @param {number} year the year within which that month can be found 191 * @returns {number} the number of days within the given month in the given year, or 192 * 0 for an invalid month in the year 193 */ 194 HebrewCal.prototype.getMonLength = function(month, year) { 195 if (month < 1 || month > 13 || (month == 13 && !this.isLeapYear(year))) { 196 return 0; 197 } 198 return this.lastDayOfMonth(month, year); 199 }; 200 201 /** 202 * Return true if the given year is a leap year in the Hebrew calendar. 203 * The year parameter may be given as a number, or as a HebrewDate object. 204 * @param {number|Object} year the year for which the leap year information is being sought 205 * @returns {boolean} true if the given year is a leap year 206 */ 207 HebrewCal.prototype.isLeapYear = function(year) { 208 var y = (typeof(year) == 'number') ? year : year.year; 209 return (MathUtils.mod(1 + 7 * y, 19) < 7); 210 }; 211 212 /** 213 * Return the type of this calendar. 214 * 215 * @returns {string} the name of the type of this calendar 216 */ 217 HebrewCal.prototype.getType = function() { 218 return this.type; 219 }; 220 221 222 /*register this calendar for the factory method */ 223 Calendar._constructors["hebrew"] = HebrewCal; 224 225 module.exports = HebrewCal; 226