Last active
December 5, 2016 02:49
-
-
Save nafg/1bd293314e0dbce97e01b96c29e1d300 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Zmanim Java API | |
* Copyright (C) 2011 - 2014 Eliyahu Hershfeld | |
* Copyright (C) September 2002 Avrom Finkelstien | |
* | |
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General | |
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) | |
* any later version. | |
* | |
* This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied | |
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | |
* details. | |
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to | |
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA, | |
* or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html | |
*//* | |
* Zmanim Java API | |
* Copyright (C) 2011 - 2014 Eliyahu Hershfeld | |
* Copyright (C) September 2002 Avrom Finkelstien | |
* | |
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General | |
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) | |
* any later version. | |
* | |
* This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied | |
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | |
* details. | |
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to | |
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA, | |
* or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html | |
*/ | |
package platinum | |
import java.util.Date | |
import java.util.Calendar | |
import java.util.GregorianCalendar | |
/** | |
* The JewishDate class allows one to maintain an instance of a Gregorian date along with the corresponding Jewish date. | |
* This class can use the standard Java Date and Calendar classes for setting it, but does not subclass these classes or | |
* use them internally to any extensive use. This class also does not have a concept of a time (which the Date class | |
* does). Please note that the calendar does not currently support dates prior to 1/1/1 Gregorian. Also keep in mind | |
* that the Gregorian calendar started on October 15, 1582, so any calculations prior to that are suspect (at least from | |
* a Gregorian perspective). While 1/1/1 Gregorian and forward are technically supported, any calculations prior to <a | |
* href="http://en.wikipedia.org/wiki/Hillel_II">Hillel II's (Hakatan's</a>) calendar (4119 in the Jewish Calendar / 359 | |
* CE Julian as recorded by <a href="http://en.wikipedia.org/wiki/Hai_Gaon">Rav Hai Gaon</a>) would be just an | |
* approximation. | |
* | |
* This open source Java code was written by <a href="http://www.facebook.com/avromf">Avrom Finkelstien</a> from his C++ | |
* code. It was refactored to fit the KosherJava Zmanim API with simplification of the code, enhancements and some bug | |
* fixing. | |
* | |
* Some of Avrom's original C++ code was translated from <a href="http://emr.cs.uiuc.edu/~reingold/calendar.C">C/C++ | |
* code</a> in <a href="http://www.calendarists.com">Calendrical Calculations</a> by Nachum Dershowitz and Edward M. | |
* Reingold, Software-- Practice & Experience, vol. 20, no. 9 (September, 1990), pp. 899- 928. Any method with the mark | |
* "ND+ER" indicates that the method was taken from this source with minor modifications. | |
* | |
* If you are looking for a class that implements a Jewish calendar version of the Calendar class, one is available from | |
* the <a href="http://site.icu-project.org/" >ICU (International Components for Unicode)</a> project, formerly part of | |
* IBM's DeveloperWorks. | |
* | |
* @see net.sourceforge.zmanim.hebrewcalendar.JewishCalendar | |
* @see net.sourceforge.zmanim.hebrewcalendar.HebrewDateFormatter | |
* @see java.util.Date | |
* @see java.util.Calendar | |
* @author © Avrom Finkelstien 2002 | |
* @author © Eliyahu Hershfeld 2011 - 2015 | |
* @version 0.2.6 | |
*/ | |
object JewishDate { | |
/** | |
* Value of the month field indicating Nissan, the first numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 7th (or 8th in a [[isJewishLeapYear()]]) month of the year. | |
*/ | |
val NISSAN: Int = 1 | |
/** | |
* Value of the month field indicating Iyar, the second numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 8th (or 9th in a [[isJewishLeapYear()]]) month of the year. | |
*/ | |
val IYAR: Int = 2 | |
/** | |
* Value of the month field indicating Sivan, the third numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 9th (or 10th in a [[isJewishLeapYear()]]) month of the year. | |
*/ | |
val SIVAN: Int = 3 | |
/** | |
* Value of the month field indicating Tammuz, the fourth numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 10th (or 11th in a [[isJewishLeapYear()]]) month of the year. | |
*/ | |
val TAMMUZ: Int = 4 | |
/** | |
* Value of the month field indicating Av, the fifth numeric month of the year in the Jewish calendar. With the year | |
* starting at [[TISHREI]], it would actually be the 11th (or 12th in a [[isJewishLeapYear()]]) | |
* month of the year. | |
*/ | |
val AV: Int = 5 | |
/** | |
* Value of the month field indicating Elul, the sixth numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 12th (or 13th in a [[isJewishLeapYear()]]) month of the year. | |
*/ | |
val ELUL: Int = 6 | |
/** | |
* Value of the month field indicating Tishrei, the seventh numeric month of the year in the Jewish calendar. With | |
* the year starting at this month, it would actually be the 1st month of the year. | |
*/ | |
val TISHREI: Int = 7 | |
/** | |
* Value of the month field indicating Cheshvan/marcheshvan, the eighth numeric month of the year in the Jewish | |
* calendar. With the year starting at [[TISHREI]], it would actually be the 2nd month of the year. | |
*/ | |
val CHESHVAN: Int = 8 | |
/** | |
* Value of the month field indicating Kislev, the ninth numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 3rd month of the year. | |
*/ | |
val KISLEV: Int = 9 | |
/** | |
* Value of the month field indicating Teves, the tenth numeric month of the year in the Jewish calendar. With the | |
* year starting at [[TISHREI]], it would actually be the 4th month of the year. | |
*/ | |
val TEVES: Int = 10 | |
/** | |
* Value of the month field indicating Shevat, the eleventh numeric month of the year in the Jewish calendar. With | |
* the year starting at [[TISHREI]], it would actually be the 5th month of the year. | |
*/ | |
val SHEVAT: Int = 11 | |
/** | |
* Value of the month field indicating Adar (or Adar I in a [[isJewishLeapYear()]]), the twelfth | |
* numeric month of the year in the Jewish calendar. With the year starting at [[TISHREI]], it would actually | |
* be the 6th month of the year. | |
*/ | |
val ADAR: Int = 12 | |
/** | |
* Value of the month field indicating Adar II, the leap (intercalary or embolismic) thirteenth (Undecimber) numeric | |
* month of the year added in Jewish [[isJewishLeapYear()]]). The leap years are years 3, 6, 8, 11, | |
* 14, 17 and 19 of a 19 year cycle. With the year starting at [[TISHREI]], it would actually be the 7th month | |
* of the year. | |
*/ | |
val ADAR_II: Int = 13 | |
/** | |
* the Jewish epoch using the RD (Rata Die/Fixed Date or Reingold Dershowitz) day used in Calendrical Calculations. | |
* Day 1 is January 1, 0001 Gregorian | |
*/ | |
private val JEWISH_EPOCH: Int = -1373429 | |
private val CHALAKIM_PER_MINUTE: Int = 18 | |
private val CHALAKIM_PER_HOUR: Int = 1080 | |
private val CHALAKIM_PER_DAY: Int = 25920 | |
// 24 * 1080 | |
private val CHALAKIM_PER_MONTH: Long = 765433 // (29 * 24 + 12) * 1080 + 793 | |
/** | |
* Days from the beginning of Sunday till molad BaHaRaD. Calculated as 1 day, 5 hours and 204 chalakim = (24 + 5) * | |
* 1080 + 204 = 31524 | |
*/ | |
private val CHALAKIM_MOLAD_TOHU: Int = 31524 | |
/** | |
* A short year where both [[CHESHVAN]] and [[KISLEV]] are 29 days. | |
* | |
* @see #getCheshvanKislevKviah() | |
* @see HebrewDateFormatter#getFormattedKviah(int) | |
*/ | |
val CHASERIM: Int = 0 | |
/** | |
* An ordered year where [[CHESHVAN]] is 29 days and [[KISLEV]] is 30 days. | |
* | |
* @see #getCheshvanKislevKviah() | |
* @see HebrewDateFormatter#getFormattedKviah(int) | |
*/ | |
val KESIDRAN: Int = 1 | |
/** | |
* A long year where both [[CHESHVAN]] and [[KISLEV]] are 30 days. | |
* | |
* @see #getCheshvanKislevKviah() | |
* @see HebrewDateFormatter#getFormattedKviah(int) | |
*/ | |
val SHELAIMIM: Int = 2 | |
/** | |
* Returns the number of days in a given month in a given month and year. | |
* | |
* @param month | |
* the month. As with other cases in this class, this is 1-based, not zero-based. | |
* @param year | |
* the year (only impacts February) | |
* @return the number of days in the month in the given year | |
*/ | |
private def getLastDayOfGregorianMonth(month: Int, year: Int): Int = { | |
month match { | |
case 2 => | |
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { | |
29 | |
} | |
else { | |
28 | |
} | |
case 4 | 6 | 9 | 11 => | |
30 | |
case _ => | |
31 | |
} | |
} | |
/** | |
* Computes the absolute date from a Gregorian date. ND+ER | |
* | |
* @param year | |
* the Gregorian year | |
* @param month | |
* the Gregorian month. Unlike the Java Calendar where January has the value of 0,This expects a 1 for | |
* January | |
* @param dayOfMonth | |
* the day of the month (1st, 2nd, etc...) | |
* @return the absolute Gregorian day | |
*/ | |
private def gregorianDateToAbsDate(year: Int, month: Int, dayOfMonth: Int): Int = { | |
var absDate: Int = dayOfMonth | |
var m: Int = month - 1 | |
while (m > 0) { | |
{ | |
absDate += getLastDayOfGregorianMonth(m, year) // days in prior months of the year | |
} | |
{ | |
m -= 1 | |
m + 1 | |
} | |
} | |
(absDate // days this year | |
+ 365 * (year - 1) // days in previous years ignoring leap days | |
+ (year - 1) / 4 // Julian leap days before this year | |
- (year - 1) / 100 // minus prior century years | |
+ (year - 1) / 400) // plus prior years divisible by 400 | |
} | |
/** | |
* Returns if the year is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19 year cycle are leap years. | |
* | |
* @param year | |
* the Jewish year. | |
* @return true if it is a leap year | |
* @see #isJewishLeapYear() | |
*/ | |
private def isJewishLeapYear(year: Int): Boolean = { | |
((7 * year) + 1) % 19 < 7 | |
} | |
/** | |
* Returns the last month of a given Jewish year. This will be 12 on a non [[isJewishLeapYear(int)]] | |
* or 13 on a leap year. | |
* | |
* @param year | |
* the Jewish year. | |
* @return 12 on a non leap year or 13 on a leap year | |
* @see #isJewishLeapYear(int) | |
*/ | |
def getLastMonthOfJewishYear(year: Int): Int = { | |
if (isJewishLeapYear(year)) ADAR_II | |
else ADAR | |
} | |
/** | |
* Returns the number of days elapsed from the Sunday prior to the start of the Jewish calendar to the mean | |
* conjunction of Tishri of the Jewish year. | |
* | |
* @param year | |
* the Jewish year | |
* @return the number of days elapsed from prior to the molad Tohu BaHaRaD (Be = Monday, Ha= 5 hours and Rad =204 | |
* chalakim/parts) prior to the start of the Jewish calendar, to the mean conjunction of Tishri of the | |
* Jewish year. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 chalakim after sunset on Sunday | |
* evening). | |
*/ | |
def getJewishCalendarElapsedDays(year: Int): Int = { | |
val chalakimSince: Long = getChalakimSinceMoladTohu(year, JewishDate.TISHREI) | |
val moladDay: Int = (chalakimSince / CHALAKIM_PER_DAY.toLong).toInt | |
val moladParts: Int = (chalakimSince - moladDay * CHALAKIM_PER_DAY.toLong).toInt | |
// delay Rosh Hashana for the 4 dechiyos | |
addDechiyos(year, moladDay, moladParts) | |
} | |
/** | |
* Adds the 4 dechiyos for molad Tishrei. These are: | |
* <ol> | |
* <li>Lo ADU Rosh - Rosh Hashana can't fall on a Sunday, Wednesday or Friday. If the molad fell on one of these | |
* days, Rosh Hashana is delayed to the following day.</li> | |
* <li>Molad Zaken - If the molad of Tishrei falls after 12 noon, Rosh Hashana is delayed to the following day. If | |
* the following day is ADU, it will be delayed an additional day.</li> | |
* <li>GaTRaD - If on a non leap year the molad of Tishrei falls on a Tuesday (Ga) on or after 9 hours (T) and 204 | |
* chalakim (TRaD) it is delayed till Thursday (one day delay, plus one day for Lo ADU Rosh)</li> | |
* <li>BeTuTaKFoT - if the year following a leap year falls on a Monday (Be) on or after 15 hours (Tu) and 589 | |
* chalakim (TaKFoT) it is delayed till Tuesday</li> | |
* </ol> | |
* | |
* @param year | |
* @param moladDay | |
* @param moladParts | |
* @return the nimber of elapsed days in the JewishCalendar adjusted for the 4 dechiyos. | |
*/ | |
private def addDechiyos(year: Int, moladDay: Int, moladParts: Int): Int = { | |
var roshHashanaDay: Int = moladDay // if no dechiyos | |
// delay Rosh Hashana for the dechiyos of the Molad - new moon 1 - Molad Zaken, 2- GaTRaD 3- BeTuTaKFoT | |
if ((moladParts >= 19440) // Dechiya of Molad Zaken - molad is >= midday (18 hours * 1080 chalakim) | |
|| (((moladDay % 7) == 2) // start Dechiya of GaTRaD - Ga = is a Tuesday | |
&& (moladParts >= 9924) // TRaD = 9 hours, 204 parts or later (9 * 1080 + 204) | |
&& !isJewishLeapYear(year)) // of a non-leap year - end Dechiya of GaTRaD | |
|| (((moladDay % 7) == 1) // start Dechiya of BeTuTaKFoT - Be = is on a Monday | |
&& (moladParts >= 16789) // TRaD = 15 hours, 589 parts or later (15 * 1080 + 589) | |
&& isJewishLeapYear(year - 1))) { | |
// in a year following a leap year - end Dechiya of BeTuTaKFoT | |
roshHashanaDay += 1 // Then postpone Rosh HaShanah one day | |
} | |
// start 4th Dechiya - Lo ADU Rosh - Rosh Hashana can't occur on A- sunday, D- Wednesday, U - Friday | |
if (((roshHashanaDay % 7) == 0) // If Rosh HaShanah would occur on Sunday, | |
|| ((roshHashanaDay % 7) == 3) // or Wednesday, | |
|| ((roshHashanaDay % 7) == 5)) { | |
// or Friday - end 4th Dechiya - Lo ADU Rosh | |
roshHashanaDay = roshHashanaDay + 1 // Then postpone it one (more) day | |
} | |
roshHashanaDay | |
} | |
/** | |
* Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu to the year | |
* and month passed in. | |
* | |
* @param year | |
* the Jewish year | |
* @param month | |
* the Jewish month the Jewish month, with the month numbers starting from Nisan. Use the JewishDate | |
* constants such as { @link JewishDate#TISHREI}. | |
* @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu | |
*/ | |
private def getChalakimSinceMoladTohu(year: Int, month: Int): Long = { | |
// Jewish lunar month = 29 days, 12 hours and 793 chalakim | |
// chalakim since Molad Tohu BeHaRaD - 1 day, 5 hours and 204 chalakim | |
val monthOfYear: Int = getJewishMonthOfYear(year, month) | |
val monthsElapsed: Int = (235 * ((year - 1) / 19) // Months in complete 19 year lunar (Metonic) cycles so far | |
+ (12 * ((year - 1) % 19)) // Regular months in this cycle | |
+ ((7 * ((year - 1) % 19) + 1) / 19) // Leap months this cycle | |
+ (monthOfYear - 1)) // add elapsed months till the start of the molad of the month | |
// return chalakim prior to BeHaRaD + number of chalakim since | |
CHALAKIM_MOLAD_TOHU + (CHALAKIM_PER_MONTH * monthsElapsed) | |
} | |
/** | |
* Converts the the [[JewishDate#NISSAN]] based constants used by this class to numeric month starting from | |
* [[JewishDate#TISHREI]]. This is required for Molad claculations. | |
* | |
* @param year | |
* The Jewish year | |
* @param month | |
* The Jewish Month | |
* @return the Jewish month of the year starting with Tishrei | |
*/ | |
private def getJewishMonthOfYear(year: Int, month: Int): Int = { | |
val isLeapYear: Boolean = JewishDate.isJewishLeapYear(year) | |
(month + (if (isLeapYear) 6 | |
else 5)) % (if (isLeapYear) 13 | |
else 12) + 1 | |
} | |
/** | |
* Validates the components of a Jewish date for validity. It will throw an [[IllegalArgumentException]] if the | |
* Jewish date is earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a | |
* [[isJewishLeapYear(int)]]), the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 > | |
* 59 or chalakim < 0 > 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into | |
* minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793/TaShTzaG). | |
* | |
* @param year | |
* the Jewish year to validate. It will reject any year <= 3761 (lower than the year 1 Gregorian). | |
* @param month | |
* the Jewish month to validate. It will reject a month < 1 or > 12 (or 13 on a leap year) . | |
* @param dayOfMonth | |
* the day of the Jewish month to validate. It will reject any value < 1 or > 30 TODO: check calling | |
* methods to see if there is any reason that the class can validate that 30 is invalid for some months. | |
* @param hours | |
* the hours (for molad calculations). It will reject an hour < 0 or > 23 | |
* @param minutes | |
* the minutes (for molad calculations). It will reject a minute < 0 or > 59 | |
* @param chalakim | |
* the chalakim/parts (for molad calculations). It will reject a chalakim < 0 or > 17. For larger numbers | |
* such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim per minutes, so it would be 44 | |
* minutes and 1 chelek in the case of 793/TaShTzaG) | |
* @throws IllegalArgumentException | |
* if a A Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a | |
* leap year), the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 > 59 or chalakim < 0 > | |
* 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes (18 | |
* chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG). | |
*/ | |
private def validateJewishDate(year: Int, month: Int, dayOfMonth: Int, hours: Int, minutes: Int, chalakim: Int): Unit = { | |
if (month < NISSAN || month > getLastMonthOfJewishYear(year)) { | |
throw new IllegalArgumentException("The Jewish month has to be between 1 and 12 (or 13 on a leap year). " + month + " is invalid for the year " + year + ".") | |
} | |
if (dayOfMonth < 1 || dayOfMonth > 30) { | |
throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > 30. " + dayOfMonth + " is invalid.") | |
} | |
// reject dates prior to 18 Teves, 3761 (1/1/1 AD). This restriction can be relaxed if the date coding is | |
// changed/corrected | |
if ((year < 3761) || (year == 3761 && (month >= TISHREI && month < TEVES)) || (year == 3761 && month == TEVES && dayOfMonth < 18)) { | |
throw new IllegalArgumentException("A Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian) can't be set. " + year + ", " + month + ", " + dayOfMonth + " is invalid.") | |
} | |
if (hours < 0 || hours > 23) { | |
throw new IllegalArgumentException("Hours < 0 > 23 can't be set. " + hours + " is invalid.") | |
} | |
if (minutes < 0 || minutes > 59) { | |
throw new IllegalArgumentException("Minutes < 0 > 59 can't be set. " + minutes + " is invalid.") | |
} | |
if (chalakim < 0 || chalakim > 17) { | |
throw new IllegalArgumentException("Chalakim/parts < 0 > 17 can't be set. " + chalakim + " is invalid. For larger numbers such as 793 (TaShTzaG) break the chalakim into minutes (18 chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG)") | |
} | |
} | |
/** | |
* Validates the components of a Gregorian date for validity. It will throw an [[IllegalArgumentException]] if a | |
* year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in. | |
* | |
* @param year | |
* the Gregorian year to validate. It will reject any year < 1. | |
* @param month | |
* the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a | |
* { @link GregorianCalendar}, where { @link Calendar#JANUARY} has a value of 0. | |
* @param dayOfMonth | |
* the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 | |
* since calling methods will simply set it to the maximum for that month. TODO: check calling methods to | |
* see if there is any reason that the class needs days > the maximum. | |
* @throws IllegalArgumentException | |
* if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in | |
* @see #validateGregorianYear(int) | |
* @see #validateGregorianMonth(int) | |
* @see #validateGregorianDayOfMonth(int) | |
*/ | |
private def validateGregorianDate(year: Int, month: Int, dayOfMonth: Int): Unit = { | |
validateGregorianMonth(month) | |
validateGregorianDayOfMonth(dayOfMonth) | |
validateGregorianYear(year) | |
} | |
/** | |
* Validates a Gregorian month for validity. | |
* | |
* @param month | |
* the Gregorian month number to validate. It will enforce that the month is between 0 - 11 like a | |
* { @link GregorianCalendar}, where { @link Calendar#JANUARY} has a value of 0. | |
*/ | |
private def validateGregorianMonth(month: Int): Unit = { | |
if (month > 11 || month < 0) { | |
throw new IllegalArgumentException("The Gregorian month has to be between 0 - 11. " + month + " is invalid.") | |
} | |
} | |
/** | |
* Validates a Gregorian day of month for validity. | |
* | |
* @param dayOfMonth | |
* the day of the Gregorian month to validate. It will reject any value < 1, but will allow values > 31 | |
* since calling methods will simply set it to the maximum for that month. TODO: check calling methods to | |
* see if there is any reason that the class needs days > the maximum. | |
*/ | |
private def validateGregorianDayOfMonth(dayOfMonth: Int): Unit = { | |
if (dayOfMonth <= 0) { | |
throw new IllegalArgumentException("The day of month can't be less than 1. " + dayOfMonth + " is invalid.") | |
} | |
} | |
/** | |
* Validates a Gregorian year for validity. | |
* | |
* @param year | |
* the Gregorian year to validate. It will reject any year < 1. | |
*/ | |
private def validateGregorianYear(year: Int): Unit = { | |
if (year < 1) { | |
throw new IllegalArgumentException("Years < 1 can't be claculated. " + year + " is invalid.") | |
} | |
} | |
/** | |
* Returns the number of days for a given Jewish year. ND+ER | |
* | |
* @param year | |
* the Jewish year | |
* @return the number of days for a given Jewish year. | |
* @see #isCheshvanLong() | |
* @see #isKislevShort() | |
*/ | |
def getDaysInJewishYear(year: Int): Int = { | |
getJewishCalendarElapsedDays(year + 1) - getJewishCalendarElapsedDays(year) | |
} | |
/** | |
* Returns if Cheshvan is long in a given Jewish year. The method name isLong is done since in a Kesidran (ordered) | |
* year Cheshvan is short. ND+ER | |
* | |
* @param year | |
* the year | |
* @return true if Cheshvan is long in Jewish year. | |
* @see #isCheshvanLong() | |
* @see #getCheshvanKislevKviah() | |
*/ | |
private def isCheshvanLong(year: Int): Boolean = { | |
getDaysInJewishYear(year) % 10 == 5 | |
} | |
/** | |
* Returns if Kislev is short (29 days VS 30 days) in a given Jewish year. The method name isShort is done since in | |
* a Kesidran (ordered) year Kislev is long. ND+ER | |
* | |
* @param year | |
* the Jewish year | |
* @return true if Kislev is short for the given Jewish year. | |
* @see #isKislevShort() | |
* @see #getCheshvanKislevKviah() | |
*/ | |
private def isKislevShort(year: Int): Boolean = { | |
getDaysInJewishYear(year) % 10 == 3 | |
} | |
/** | |
* Returns the number of days of a Jewish month for a given month and year. | |
* | |
* @param month | |
* the Jewish month | |
* @param year | |
* the Jewish Year | |
* @return the number of days for a given Jewish month | |
*/ | |
private def getDaysInJewishMonth(month: Int, year: Int): Int = { | |
if ((month == IYAR) || | |
(month == TAMMUZ) || | |
(month == ELUL) || | |
((month == CHESHVAN) && !isCheshvanLong(year)) || | |
((month == KISLEV) && isKislevShort(year)) || | |
(month == TEVES) || | |
((month == ADAR) && !isJewishLeapYear(year)) || | |
(month == ADAR_II)) { | |
// println(s"getDaysInJewishMonth($month, $year) = 29") | |
29 | |
} | |
else { | |
// println(s"getDaysInJewishMonth($month, $year) = 30") | |
30 | |
} | |
} | |
/** | |
* Returns the absolute date of Jewish date. ND+ER | |
* | |
* @param year | |
* the Jewish year. The year can't be negative | |
* @param month | |
* the Jewish month starting with Nisan. Nisan expects a value of 1 etc till Adar with a value of 12. For | |
* a leap year, 13 will be the expected value for Adar II. Use the constants { @link JewishDate#NISSAN} | |
* etc. | |
* @param dayOfMonth | |
* the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only | |
* has 29 days, the day will be set as 29. | |
* @return the absolute date of the Jewish date. | |
*/ | |
def jewishDateToAbsDate(year: Int, month: Int, dayOfMonth: Int): Int = { | |
val elapsed: Int = getDaysSinceStartOfJewishYear(year, month, dayOfMonth) | |
// add elapsed days this year + Days in prior years + Days elapsed before absolute year 1 | |
elapsed + getJewishCalendarElapsedDays(year) + JEWISH_EPOCH | |
} | |
/** | |
* Returns the number of days from the Jewish epoch from the number of chalakim from the epoch passed in. | |
* | |
* @param chalakim | |
* the number of chalakim since the beginning of Sunday prior to BaHaRaD | |
* @return the number of days from the Jewish epoch | |
*/ | |
private def moladToAbsDate(chalakim: Long): Int = { | |
(chalakim / CHALAKIM_PER_DAY).toInt + JEWISH_EPOCH | |
} | |
/** | |
* returns the number of days from Rosh Hashana of the date passed in, till the full date passed in. | |
* | |
* @param year | |
* the Jewish year | |
* @param month | |
* the Jewish month | |
* @param dayOfMonth | |
* the day in the Jewish month | |
* @return the number of days | |
*/ | |
def getDaysSinceStartOfJewishYear(year: Int, month: Int, dayOfMonth: Int): Int = { | |
var elapsedDays: Int = dayOfMonth | |
// Before Tishrei (from Nissan to Tishrei), add days in prior months | |
if (month < TISHREI) { | |
// this year before and after Nisan. | |
var m: Int = TISHREI | |
while (m <= getLastMonthOfJewishYear(year)) { | |
{ | |
elapsedDays += getDaysInJewishMonth(m, year) | |
} | |
{ | |
m += 1 | |
m - 1 | |
} | |
} | |
// println(elapsedDays) | |
m = NISSAN | |
while (m < month) { | |
{ | |
elapsedDays += getDaysInJewishMonth(m, year) | |
} | |
{ | |
m += 1 | |
m - 1 | |
} | |
} | |
} | |
else { | |
// Add days in prior months this year | |
var m: Int = TISHREI | |
while (m < month) { | |
{ | |
elapsedDays += getDaysInJewishMonth(m, year) | |
} | |
{ | |
m += 1 | |
m - 1 | |
} | |
} | |
} | |
elapsedDays | |
} | |
/** | |
* Computes the Gregorian date from the absolute date. ND+ER | |
*/ | |
def absDateToDate(absDate: Int) = { | |
var year: Int = absDate / 366 // Search forward year by year from approximate year | |
while (absDate >= JewishDate.gregorianDateToAbsDate(year + 1, 1, 1)) { | |
{ | |
year += 1 | |
} | |
} | |
var month: Int = 1 // Search forward month by month from January | |
while (absDate > JewishDate.gregorianDateToAbsDate(year, month, JewishDate.getLastDayOfGregorianMonth(month, year))) { | |
{ | |
month += 1 | |
} | |
} | |
val dayOfMonth: Int = absDate - JewishDate.gregorianDateToAbsDate(year, month, 1) + 1 | |
(year, month, dayOfMonth) | |
} | |
} | |
class JewishDate() | |
/** | |
* Default constructor will set a default date to the current system date. | |
*/ | |
extends Comparable[JewishDate] with Cloneable { | |
resetDate() | |
private var jewishMonth: Int = 0 | |
private var jewishDay: Int = 0 | |
private var jewishYear: Int = 0 | |
private var moladHours: Int = 0 | |
private var moladMinutes: Int = 0 | |
private var moladChalakim: Int = 0 | |
/** | |
* Returns the molad hours. Only a JewishDate object populated with [[getMolad()]], | |
* [[setJewishDate(int,]] or [[setMoladHours(int)]] will have this field | |
* populated. A regular JewishDate object will have this field set to 0. | |
* | |
* @return the molad hours | |
* @see #setMoladHours(int) | |
* @see #getMolad() | |
* @see #setJewishDate(int, int, int, int, int, int) | |
*/ | |
def getMoladHours: Int = { | |
moladHours | |
} | |
/** | |
* Sets the molad hours. | |
* | |
* @param moladHours | |
* the molad hours to set | |
* @see #getMoladHours() | |
* @see #getMolad() | |
* @see #setJewishDate(int, int, int, int, int, int) | |
* | |
*/ | |
def setMoladHours(moladHours: Int): Unit = { | |
this.moladHours = moladHours | |
} | |
/** | |
* Returns the molad minutes. Only an object populated with [[getMolad()]], | |
* [[setJewishDate(int,]] or or [[setMoladMinutes(int)]] will have these fields | |
* populated. A regular JewishDate object will have this field set to 0. | |
* | |
* @return the molad minutes | |
* @see #setMoladMinutes(int) | |
* @see #getMolad() | |
* @see #setJewishDate(int, int, int, int, int, int) | |
*/ | |
def getMoladMinutes: Int = { | |
moladMinutes | |
} | |
/** | |
* Sets the molad minutes. The expectation is that the traditional minute-less chalakim will be broken out to | |
* minutes and [[setMoladChalakim(int)]] , so 793 (TaShTZaG) parts would have the minutes set to | |
* 44 and chalakim to 1. | |
* | |
* @param moladMinutes | |
* the molad minutes to set | |
* @see #getMoladMinutes() | |
* @see #setMoladChalakim(int) | |
* @see #getMolad() | |
* @see #setJewishDate(int, int, int, int, int, int) | |
* | |
*/ | |
def setMoladMinutes(moladMinutes: Int): Unit = { | |
this.moladMinutes = moladMinutes | |
} | |
/** | |
* Sets the molad chalakim/parts. The expectation is that the traditional minute-less chalakim will be broken out to | |
* [[setMoladMinutes(int)]] and chalakim, so 793 (TaShTZaG) parts would have the minutes set to 44 and | |
* chalakim to 1. | |
* | |
* @param moladChalakim | |
* the molad chalakim/parts to set | |
* @see #getMoladChalakim() | |
* @see #setMoladMinutes(int) | |
* @see #getMolad() | |
* @see #setJewishDate(int, int, int, int, int, int) | |
* | |
*/ | |
def setMoladChalakim(moladChalakim: Int): Unit = { | |
this.moladChalakim = moladChalakim | |
} | |
/** | |
* Returns the molad chalakim/parts. Only an object populated with [[getMolad()]], | |
* [[setJewishDate(int,]] or or [[setMoladChalakim(int)]] will have these fields | |
* populated. A regular JewishDate object will have this field set to 0. | |
* | |
* @return the molad chalakim/parts | |
* @see #setMoladChalakim(int) | |
* @see #getMolad() | |
* @see #setJewishDate(int, int, int, int, int, int) | |
*/ | |
def getMoladChalakim: Int = { | |
moladChalakim | |
} | |
/** | |
* The month, where 1 == January, 2 == February, etc... Note that this is different than the Java's Calendar class | |
* where January ==0 | |
*/ | |
private var gregorianMonth: Int = 0 | |
/** The day of the Gregorian month */ | |
private var gregorianDayOfMonth: Int = 0 | |
/** The Gregorian year */ | |
private var gregorianYear: Int = 0 | |
/** 1 == Sunday, 2 == Monday, etc... */ | |
private var dayOfWeek: Int = 0 | |
private var gregorianAbsDate: Int = 0 | |
/** | |
* Computes the Gregorian date from the absolute date. ND+ER | |
*/ | |
private def absDateToDate(absDate: Int): Unit = { | |
var year: Int = absDate / 366 // Search forward year by year from approximate year | |
while (absDate >= JewishDate.gregorianDateToAbsDate(year + 1, 1, 1)) { | |
{ | |
year += 1 | |
} | |
} | |
var month: Int = 1 // Search forward month by month from January | |
while (absDate > JewishDate.gregorianDateToAbsDate(year, month, JewishDate.getLastDayOfGregorianMonth(month, year))) { | |
{ | |
month += 1 | |
} | |
} | |
val dayOfMonth: Int = absDate - JewishDate.gregorianDateToAbsDate(year, month, 1) + 1 | |
setInternalGregorianDate(year, month, dayOfMonth) | |
} | |
/** | |
* Returns the absolute date (days since January 1, 0001 on the Gregorian calendar). | |
* | |
* @return the number of days since January 1, 1 | |
*/ | |
protected def getAbsDate: Int = { | |
gregorianAbsDate | |
} | |
/** | |
* Returns if the year the calendar is set to is a Jewish leap year. Years 3, 6, 8, 11, 14, 17 and 19 in the 19 year | |
* cycle are leap years. | |
* | |
* @return true if it is a leap year | |
* @see #isJewishLeapYear(int) | |
*/ | |
def isJewishLeapYear: Boolean = { | |
JewishDate.isJewishLeapYear(getJewishYear) | |
} | |
/** | |
* Returns the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu to the Jewish | |
* year and month that this Object is set to. | |
* | |
* @return the number of chalakim (parts - 1080 to the hour) from the original hypothetical Molad Tohu | |
*/ | |
def getChalakimSinceMoladTohu: Long = { | |
JewishDate.getChalakimSinceMoladTohu(jewishYear, jewishMonth) | |
} | |
/** | |
* Returns the number of days for the current year that the calendar is set to. | |
* | |
* @return the number of days for the Object's current Jewish year. | |
* @see #isCheshvanLong() | |
* @see #isKislevShort() | |
* @see #isJewishLeapYear() | |
*/ | |
def getDaysInJewishYear: Int = { | |
JewishDate.getDaysInJewishYear(getJewishYear) | |
} | |
/** | |
* Returns if Cheshvan is long (30 days VS 29 days) for the current year that the calendar is set to. The method | |
* name isLong is done since in a Kesidran (ordered) year Cheshvan is short. | |
* | |
* @return true if Cheshvan is long for the current year that the calendar is set to | |
* @see #isCheshvanLong() | |
*/ | |
def isCheshvanLong: Boolean = { | |
JewishDate.isCheshvanLong(getJewishYear) | |
} | |
/** | |
* Returns if the Kislev is short for the year that this class is set to. The method name isShort is done since in a | |
* Kesidran (ordered) year Kislev is long. | |
* | |
* @return true if Kislev is short for the year that this class is set to | |
*/ | |
def isKislevShort: Boolean = { | |
JewishDate.isKislevShort(getJewishYear) | |
} | |
/** | |
* Returns the Cheshvan and Kislev kviah (whether a Jewish year is short, regular or long). It will return | |
* [[SHELAIMIM]] if both cheshvan and kislev are 30 days, [[KESIDRAN]] if Cheshvan is 29 days and Kislev | |
* is 30 days and [[CHASERIM]] if both are 29 days. | |
* | |
* @return { @link #SHELAIMIM} if both cheshvan and kislev are 30 days, { @link #KESIDRAN} if Cheshvan is 29 days and | |
* Kislev is 30 days and { @link #CHASERIM} if both are 29 days. | |
* @see #isCheshvanLong() | |
* @see #isKislevShort() | |
*/ | |
def getCheshvanKislevKviah: Int = { | |
if (isCheshvanLong && !isKislevShort) { | |
JewishDate.SHELAIMIM | |
} | |
else if (!isCheshvanLong && isKislevShort) { | |
JewishDate.CHASERIM | |
} | |
else { | |
JewishDate.KESIDRAN | |
} | |
} | |
/** | |
* Returns the number of days of the Jewish month that the calendar is currently set to. | |
* | |
* @return the number of days for the Jewish month that the calendar is currently set to. | |
*/ | |
def getDaysInJewishMonth: Int = { | |
JewishDate.getDaysInJewishMonth(getJewishMonth, getJewishYear) | |
} | |
/** | |
* Computes the Jewish date from the absolute date. ND+ER | |
*/ | |
private def absDateToJewishDate(): Unit = { | |
// Approximation from below | |
jewishYear = (gregorianAbsDate + JewishDate.JEWISH_EPOCH) / 366 | |
// Search forward for year from the approximation | |
while (gregorianAbsDate >= JewishDate.jewishDateToAbsDate(jewishYear + 1, JewishDate.TISHREI, 1)) { | |
{ | |
jewishYear += 1 | |
} | |
} | |
// Search forward for month from either Tishri or Nisan. | |
if (gregorianAbsDate < JewishDate.jewishDateToAbsDate(jewishYear, JewishDate.NISSAN, 1)) { | |
jewishMonth = JewishDate.TISHREI // Start at Tishri | |
} | |
else { | |
jewishMonth = JewishDate.NISSAN // Start at Nisan | |
} | |
while (gregorianAbsDate > JewishDate.jewishDateToAbsDate(jewishYear, jewishMonth, getDaysInJewishMonth)) { | |
{ | |
jewishMonth += 1 | |
} | |
} | |
// Calculate the day by subtraction | |
jewishDay = gregorianAbsDate - JewishDate.jewishDateToAbsDate(jewishYear, jewishMonth, 1) + 1 | |
} | |
/** | |
* Returns the molad for a given year and month. Returns a JewishDate [[Object]] set to the date of the molad | |
* with the [[getMoladHours()]], [[getMoladMinutes()]] and [[getMoladChalakim()]] set. In the current implementation, it sets the molad time based on a midnight date rollover. This | |
* means that Rosh Chodesh Adar II, 5771 with a molad of 7 chalakim past midnight on Shabbos 29 Adar I / March 5, | |
* 2011 12:00 AM and 7 chalakim, will have the following values: hours: 0, minutes: 0, Chalakim: 7. | |
* | |
* @return a JewishDate { @link Object} set to the date of the molad with the { @link #getMoladHours() hours}, | |
* { @link #getMoladMinutes() minutes} and { @link #getMoladChalakim() chalakim} set. | |
*/ | |
def getMolad: JewishDate = { | |
val moladDate: JewishDate = new JewishDate(getChalakimSinceMoladTohu) | |
if (moladDate.getMoladHours >= 6) { | |
moladDate.forward() | |
} | |
moladDate.setMoladHours((moladDate.getMoladHours + 18) % 24) | |
moladDate | |
} | |
/** | |
* Constructor that creates a JewishDate based on a molad passed in. The molad would be the number of chalakim/parts | |
* starting at the begining of Sunday prior to the molad Tohu BeHaRaD (Be = Monday, Ha= 5 hours and Rad =204 | |
* chalakim/parts) - prior to the start of the Jewish calendar. BeHaRaD is 23:11:20 on Sunday night(5 hours 204/1080 | |
* chalakim after sunset on Sunday evening). | |
* | |
* @param molad the number of chalakim since the beginning of Sunday prior to BaHaRaD | |
*/ | |
def this(molad: Long) { | |
this() | |
absDateToDate(JewishDate.moladToAbsDate(molad)) | |
// long chalakimSince = getChalakimSinceMoladTohu(year, JewishDate.TISHREI);// tishrei | |
val conjunctionDay: Int = (molad / JewishDate.CHALAKIM_PER_DAY.toLong).toInt | |
val conjunctionParts: Int = (molad - conjunctionDay * JewishDate.CHALAKIM_PER_DAY.toLong).toInt | |
setMoladTime(conjunctionParts) | |
} | |
/** | |
* Sets the molad time (hours minutes and chalakim) based on the number of chalakim since the start of the day. | |
* | |
* @param chalakim | |
* the number of chalakim since the start of the day. | |
*/ | |
private def setMoladTime(chalakim: Int): Unit = { | |
var adjustedChalakim: Int = chalakim | |
setMoladHours(adjustedChalakim / JewishDate.CHALAKIM_PER_HOUR) | |
adjustedChalakim = adjustedChalakim - (getMoladHours * JewishDate.CHALAKIM_PER_HOUR) | |
setMoladMinutes(adjustedChalakim / JewishDate.CHALAKIM_PER_MINUTE) | |
setMoladChalakim(adjustedChalakim - moladMinutes * JewishDate.CHALAKIM_PER_MINUTE) | |
} | |
/** | |
* Creates a Jewish date based on a Jewish year, month and day of month. | |
* | |
* @param jewishYear | |
* the Jewish year | |
* @param jewishMonth | |
* the Jewish month. The method expects a 1 for Nissan ... 12 for Adar and 13 for Adar II. Use the | |
* constants { @link #NISSAN} ... { @link #ADAR} (or { @link #ADAR_II} for a leap year Adar II) to avoid any | |
* confusion. | |
* @param jewishDayOfMonth | |
* the Jewish day of month. If 30 is passed in for a month with only 29 days (for example { @link #IYAR}, | |
* or { @link #KISLEV} in a year that { @link #isKislevShort()}), the 29th (last valid date of the month) | |
* will be set | |
* @throws IllegalArgumentException | |
* if the day of month is < 1 or > 30, or a year of < 0 is passed in. | |
*/ | |
def this(jewishYear: Int, jewishMonth: Int, jewishDayOfMonth: Int) { | |
this() | |
setJewishDate(jewishYear, jewishMonth, jewishDayOfMonth) | |
} | |
/** | |
* A constructor that initializes the date to the [[java.util.Date]] paremeter. | |
* | |
* @param date | |
* the <code>Date</code> to set the calendar to | |
* @throws IllegalArgumentException | |
* if the date would fall prior to the January 1, 1 AD | |
*/ | |
def this(date: Date) { | |
this() | |
setDate(date) | |
} | |
/** | |
* A constructor that initializes the date to the [[java.util.Calendar]] paremeter. | |
* | |
* @param calendar | |
* the <code>Calendar</code> to set the calendar to | |
* @throws IllegalArgumentException | |
* if the { @link Calendar#ERA} is { @link GregorianCalendar#BC} | |
*/ | |
def this(calendar: Calendar) { | |
this() | |
setDate(calendar) | |
} | |
/** | |
* Sets the date based on a [[java.util.Calendar]] object. Modifies the Jewish date as well. | |
* | |
* @param calendar | |
* the <code>Calendar</code> to set the calendar to | |
* @throws IllegalArgumentException | |
* if the { @link Calendar#ERA} is { @link GregorianCalendar#BC} | |
*/ | |
def setDate(calendar: Calendar): Unit = { | |
if (calendar.get(Calendar.ERA) == GregorianCalendar.BC) { | |
throw new IllegalArgumentException("Calendars with a BC era are not supported. The year " + calendar.get(Calendar.YEAR) + " BC is invalid.") | |
} | |
gregorianMonth = calendar.get(Calendar.MONTH) + 1 | |
gregorianDayOfMonth = calendar.get(Calendar.DATE) | |
gregorianYear = calendar.get(Calendar.YEAR) | |
gregorianAbsDate = JewishDate.gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth) // init the date | |
absDateToJewishDate() | |
dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1 // set day of week | |
} | |
/** | |
* Sets the date based on a [[java.util.Date]] object. Modifies the Jewish date as well. | |
* | |
* @param date | |
* the <code>Date</code> to set the calendar to | |
* @throws IllegalArgumentException | |
* if the date would fall prior to the year 1 AD | |
*/ | |
def setDate(date: Date): Unit = { | |
val cal: Calendar = Calendar.getInstance | |
cal.setTime(date) | |
setDate(cal) | |
} | |
/** | |
* Sets the Gregorian Date, and updates the Jewish date accordingly. Like the Java Calendar A value of 0 is expected | |
* for January. | |
* | |
* @param year | |
* the Gregorian year | |
* @param month | |
* the Gregorian month. Like the Java Calendar, this class expects 0 for January | |
* @param dayOfMonth | |
* the Gregorian day of month. If this is > the number of days in the month/year, the last valid date of | |
* the month will be set | |
* @throws IllegalArgumentException | |
* if a year of < 1, a month < 0 or > 11 or a day of month < 1 is passed in | |
*/ | |
def setGregorianDate(year: Int, month: Int, dayOfMonth: Int): Unit = { | |
JewishDate.validateGregorianDate(year, month, dayOfMonth) | |
setInternalGregorianDate(year, month + 1, dayOfMonth) | |
} | |
/** | |
* Sets the hidden internal representation of the Gregorian date , and updates the Jewish date accordingly. While | |
* public getters and setters have 0 based months matching the Java Calendar classes, This class internally | |
* represents the Gregorian month starting at 1. When this is called it will not adjust the month to match the Java | |
* Calendar classes. | |
* | |
* @param year | |
* the | |
* @param month | |
* @param dayOfMonth | |
*/ | |
private def setInternalGregorianDate(year: Int, month: Int, dayOfMonth: Int): Unit = { | |
// make sure date is a valid date for the given month, if not, set to last day of month | |
val day = | |
if (dayOfMonth > JewishDate.getLastDayOfGregorianMonth(month, year)) | |
JewishDate.getLastDayOfGregorianMonth(month, year) | |
else | |
dayOfMonth | |
// init month, date, year | |
gregorianMonth = month | |
gregorianDayOfMonth = day | |
gregorianYear = year | |
gregorianAbsDate = JewishDate.gregorianDateToAbsDate(gregorianYear, gregorianMonth, gregorianDayOfMonth) // init date | |
absDateToJewishDate() | |
dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1 | |
} | |
/** | |
* Sets the Jewish Date and updates the Gregorian date accordingly. | |
* | |
* @param year | |
* the Jewish year. The year can't be negative | |
* @param month | |
* the Jewish month starting with Nisan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for | |
* Adar II. Use the constants { @link #NISSAN} ... { @link #ADAR} (or { @link #ADAR_II} for a leap year Adar | |
* II) to avoid any confusion. | |
* @param dayOfMonth | |
* the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only | |
* has 29 days, the day will be set as 29. | |
* @throws IllegalArgumentException | |
* if a A Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a | |
* leap year) or the day of month is < 1 or > 30 is passed in | |
*/ | |
def setJewishDate(year: Int, month: Int, dayOfMonth: Int): Unit = { | |
setJewishDate(year, month, dayOfMonth, 0, 0, 0) | |
} | |
/** | |
* Sets the Jewish Date and updates the Gregorian date accordingly. | |
* | |
* @param year | |
* the Jewish year. The year can't be negative | |
* @param month | |
* the Jewish month starting with Nisan. A value of 1 is expected for Nissan ... 12 for Adar and 13 for | |
* Adar II. Use the constants { @link #NISSAN} ... { @link #ADAR} (or { @link #ADAR_II} for a leap year Adar | |
* II) to avoid any confusion. | |
* @param dayOfMonth | |
* the Jewish day of month. valid values are 1-30. If the day of month is set to 30 for a month that only | |
* has 29 days, the day will be set as 29. | |
* @param hours | |
* the hour of the day. Used for Molad calculations | |
* @param minutes | |
* the minutes. Used for Molad calculations | |
* @param chalakim | |
* the chalakim/parts. Used for Molad calculations. The chalakim should not exceed 17. Minutes should be | |
* used for larger numbers. | |
* @throws IllegalArgumentException | |
* if a A Jewish date earlier than 18 Teves, 3761 (1/1/1 Gregorian), a month < 1 or > 12 (or 13 on a | |
* leap year), the day of month is < 1 or > 30, an hour < 0 or > 23, a minute < 0 > 59 or chalakim < 0 > | |
* 17. For larger a larger number of chalakim such as 793 (TaShTzaG) break the chalakim into minutes (18 | |
* chalakim per minutes, so it would be 44 minutes and 1 chelek in the case of 793 (TaShTzaG). | |
*/ | |
def setJewishDate(year: Int, month: Int, dayOfMonth: Int, hours: Int, minutes: Int, chalakim: Int): Unit = { | |
JewishDate.validateJewishDate(year, month, dayOfMonth, hours, minutes, chalakim) | |
// if 30 is passed for a month that only has 29 days (for example by rolling the month from a month that had 30 | |
// days to a month that only has 29) set the date to 29th | |
val day = | |
if (dayOfMonth > JewishDate.getDaysInJewishMonth(month, year)) | |
JewishDate.getDaysInJewishMonth(month, year) | |
else | |
dayOfMonth | |
jewishMonth = month | |
jewishDay = day | |
jewishYear = year | |
moladHours = hours | |
moladMinutes = minutes | |
moladChalakim = chalakim | |
gregorianAbsDate = JewishDate.jewishDateToAbsDate(jewishYear, jewishMonth, jewishDay) // reset Gregorian date | |
absDateToDate(gregorianAbsDate) | |
dayOfWeek = Math.abs(gregorianAbsDate % 7) + 1 // reset day of week | |
} | |
/** | |
* Returns this object's date as a [[java.util.Calendar]] object. | |
* | |
* @return The { @link java.util.Calendar} | |
*/ | |
def getGregorianCalendar: Calendar = { | |
val calendar: Calendar = Calendar.getInstance | |
calendar.set(getGregorianYear, getGregorianMonth, getGregorianDayOfMonth) | |
calendar | |
} | |
/** | |
* Resets this date to the current system date. | |
*/ | |
def resetDate(): Unit = { | |
val calendar: Calendar = Calendar.getInstance | |
setDate(calendar) | |
} | |
/** | |
* Rolls the date forward by 1 day. It modifies both the Gregorian and Jewish dates accordingly. The API does not | |
* currently offer the ability to forward more than one day t a time, or to forward by month or year. If such | |
* manipulation is required use the [[Calendar]] class [[Calendar#add(int, int)]] or | |
* [[Calendar#roll(int, int)]] methods in the following manner. | |
* | |
* <pre> | |
* <code> | |
* Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate | |
* cal.add(Calendar.MONTH, 3); // add 3 Gregorian months | |
* jewishDate.setDate(cal); // set the updated calendar back to this class | |
* </code> | |
* </pre> | |
* | |
* @see #back() | |
* @see Calendar#add(int, int) | |
* @see Calendar#roll(int, int) | |
*/ | |
def forward(): Unit = { | |
// Change Gregorian date | |
if (gregorianDayOfMonth == JewishDate.getLastDayOfGregorianMonth(gregorianMonth, gregorianYear)) { | |
// if last day of year | |
if (gregorianMonth == 12) { | |
gregorianYear += 1 | |
gregorianMonth = 1 | |
gregorianDayOfMonth = 1 | |
} | |
else { | |
gregorianMonth += 1 | |
gregorianDayOfMonth = 1 | |
} | |
} | |
else { | |
// if not last day of month | |
gregorianDayOfMonth += 1 | |
} | |
// Change the Jewish Date | |
if (jewishDay == getDaysInJewishMonth) { | |
// if it last day of elul (i.e. last day of Jewish year) | |
if (jewishMonth == JewishDate.ELUL) { | |
jewishYear += 1 | |
jewishMonth += 1 | |
jewishDay = 1 | |
} | |
else if (jewishMonth == JewishDate.getLastMonthOfJewishYear(jewishYear)) { | |
// if it is the last day of Adar, or Adar II as case may be | |
jewishMonth = JewishDate.NISSAN | |
jewishDay = 1 | |
} | |
else { | |
jewishMonth += 1 | |
jewishDay = 1 | |
} | |
} | |
else { | |
// if not last date of month | |
jewishDay += 1 | |
} | |
if (dayOfWeek == 7) { | |
// if last day of week, loop back to Sunday | |
dayOfWeek = 1 | |
} | |
else { | |
dayOfWeek += 1 | |
} | |
gregorianAbsDate += 1 // increment the absolute date | |
} | |
/** | |
* Rolls the date back by 1 day. It modifies both the Gregorian and Jewish dates accordingly. The API does not | |
* currently offer the ability to forward more than one day t a time, or to forward by month or year. If such | |
* manipulation is required use the [[Calendar]] class [[Calendar#add(int, int)]] or | |
* [[Calendar#roll(int, int)]] methods in the following manner. | |
* | |
* <pre> | |
* <code> | |
* Calendar cal = jewishDate.getTime(); // get a java.util.Calendar representation of the JewishDate | |
* cal.add(Calendar.MONTH, -3); // subtract 3 Gregorian months | |
* jewishDate.setDate(cal); // set the updated calendar back to this class | |
* </code> | |
* </pre> | |
* | |
* @see #back() | |
* @see Calendar#add(int, int) | |
* @see Calendar#roll(int, int) | |
*/ | |
def back(): Unit = { | |
// Change Gregorian date | |
if (gregorianDayOfMonth == 1) { | |
// if first day of month | |
if (gregorianMonth == 1) { | |
// if first day of year | |
gregorianMonth = 12 | |
gregorianYear -= 1 | |
} | |
else { | |
gregorianMonth -= 1 | |
} | |
// change to last day of previous month | |
gregorianDayOfMonth = JewishDate.getLastDayOfGregorianMonth(gregorianMonth, gregorianYear) | |
} | |
else { | |
gregorianDayOfMonth -= 1 | |
} | |
// change Jewish date | |
if (jewishDay == 1) { | |
// if first day of the Jewish month | |
if (jewishMonth == JewishDate.NISSAN) { | |
jewishMonth = JewishDate.getLastMonthOfJewishYear(jewishYear) | |
} | |
else if (jewishMonth == JewishDate.TISHREI) { | |
// if Rosh Hashana | |
jewishYear -= 1 | |
jewishMonth -= 1 | |
} | |
else { | |
jewishMonth -= 1 | |
} | |
jewishDay = getDaysInJewishMonth | |
} | |
else { | |
jewishDay -= 1 | |
} | |
if (dayOfWeek == 1) { | |
// if first day of week, loop back to Saturday | |
dayOfWeek = 7 | |
} | |
else { | |
dayOfWeek -= 1 | |
} | |
gregorianAbsDate -= 1 // change the absolute date | |
} | |
/** | |
* @see Object#equals(Object) | |
*/ | |
override def equals(o: Any): Boolean = { | |
if (this eq o.asInstanceOf[AnyRef]) { | |
return true | |
} | |
if (!o.isInstanceOf[JewishDate]) { | |
return false | |
} | |
val jewishDate: JewishDate = o.asInstanceOf[JewishDate] | |
gregorianAbsDate == jewishDate.getAbsDate | |
} | |
/** | |
* Compares two dates as per the compareTo() method in the Comparable interface. Returns a value less than 0 if this | |
* date is "less than" (before) the date, greater than 0 if this date is "greater than" (after) the date, or 0 if | |
* they are equal. | |
*/ | |
def compareTo(jewishDate: JewishDate): Int = { | |
if (gregorianAbsDate < jewishDate.getAbsDate) -1 | |
else if (gregorianAbsDate > jewishDate.getAbsDate) 1 | |
else 0 | |
} | |
/** | |
* Returns the Gregorian month (between 0-11). | |
* | |
* @return the Gregorian month (between 0-11). Like the java.util.Calendar, months are 0 based. | |
*/ | |
def getGregorianMonth: Int = { | |
gregorianMonth - 1 | |
} | |
/** | |
* Returns the Gregorian day of the month. | |
* | |
* @return the Gregorian day of the mont | |
*/ | |
def getGregorianDayOfMonth: Int = { | |
gregorianDayOfMonth | |
} | |
/** | |
* Returns the Gregotian year. | |
* | |
* @return the Gregorian year | |
*/ | |
def getGregorianYear: Int = { | |
gregorianYear | |
} | |
/** | |
* Returns the Jewish month 1-12 (or 13 years in a leap year). The month count starts with 1 for Nisan and goes to | |
* 13 for Adar II | |
* | |
* @return the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nisan and | |
* goes to 13 for Adar II | |
*/ | |
def getJewishMonth: Int = { | |
jewishMonth | |
} | |
/** | |
* Returns the Jewish day of month. | |
* | |
* @return the Jewish day of the month | |
*/ | |
def getJewishDayOfMonth: Int = { | |
jewishDay | |
} | |
/** | |
* Returns the Jewish year. | |
* | |
* @return the Jewish year | |
*/ | |
def getJewishYear: Int = { | |
jewishYear | |
} | |
/** | |
* Returns the day of the week as a number between 1-7. | |
* | |
* @return the day of the week as a number between 1-7. | |
*/ | |
def getDayOfWeek: Int = { | |
dayOfWeek | |
} | |
/** | |
* Sets the Gregorian month. | |
* | |
* @param month | |
* the Gregorian month | |
* @throws IllegalArgumentException | |
* if a month < 0 or > 11 is passed in | |
*/ | |
def setGregorianMonth(month: Int): Unit = { | |
JewishDate.validateGregorianMonth(month) | |
setInternalGregorianDate(gregorianYear, month + 1, gregorianDayOfMonth) | |
} | |
/** | |
* sets the Gregorian year. | |
* | |
* @param year | |
* the Gregorian year. | |
* @throws IllegalArgumentException | |
* if a year of < 1 is passed in | |
*/ | |
def setGregorianYear(year: Int): Unit = { | |
JewishDate.validateGregorianYear(year) | |
setInternalGregorianDate(year, gregorianMonth, gregorianDayOfMonth) | |
} | |
/** | |
* sets the Gregorian Day of month. | |
* | |
* @param dayOfMonth | |
* the Gregorian Day of month. | |
* @throws IllegalArgumentException | |
* if the day of month of < 1 is passed in | |
*/ | |
def setGregorianDayOfMonth(dayOfMonth: Int): Unit = { | |
JewishDate.validateGregorianDayOfMonth(dayOfMonth) | |
setInternalGregorianDate(gregorianYear, gregorianMonth, dayOfMonth) | |
} | |
/** | |
* sets the Jewish month. | |
* | |
* @param month | |
* the Jewish month from 1 to 12 (or 13 years in a leap year). The month count starts with 1 for Nisan | |
* and goes to 13 for Adar II | |
* @throws IllegalArgumentException | |
* if a month < 1 or > 12 (or 13 on a leap year) is passed in | |
*/ | |
def setJewishMonth(month: Int): Unit = { | |
setJewishDate(jewishYear, month, jewishDay) | |
} | |
/** | |
* sets the Jewish year. | |
* | |
* @param year | |
* the Jewish year | |
* @throws IllegalArgumentException | |
* if a year of < 3761 is passed in. The same will happen if the year is 3761 and the month and day | |
* previously set are < 18 Teves (preior to Jan 1, 1 AD) | |
*/ | |
def setJewishYear(year: Int): Unit = { | |
setJewishDate(year, jewishMonth, jewishDay) | |
} | |
/** | |
* sets the Jewish day of month. | |
* | |
* @param dayOfMonth | |
* the Jewish day of month | |
* @throws IllegalArgumentException | |
* if the day of month is < 1 or > 30 is passed in | |
*/ | |
def setJewishDayOfMonth(dayOfMonth: Int): Unit = { | |
setJewishDate(jewishYear, jewishMonth, dayOfMonth) | |
} | |
/** | |
* @see Object#hashCode() | |
*/ | |
override def hashCode: Int = { | |
var result: Int = 17 | |
result = 37 * result + getClass.hashCode // needed or this and subclasses will return identical hash | |
result += 37 * result + gregorianAbsDate | |
result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment