Skip to content

Instantly share code, notes, and snippets.

@nafg
Last active December 5, 2016 02:49
Show Gist options
  • Save nafg/1bd293314e0dbce97e01b96c29e1d300 to your computer and use it in GitHub Desktop.
Save nafg/1bd293314e0dbce97e01b96c29e1d300 to your computer and use it in GitHub Desktop.
/*
* 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 &amp; 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 &copy; Avrom Finkelstien 2002
* @author &copy; 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 &lt; 1 or &gt; 30, or a year of &lt; 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 &gt; the number of days in the month/year, the last valid date of
* the month will be set
* @throws IllegalArgumentException
* if a year of &lt; 1, a month &lt; 0 or &gt; 11 or a day of month &lt; 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 &lt; 1 or &gt; 12 (or 13 on a
* leap year) or the day of month is &lt; 1 or &gt; 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 &lt; 1 or &gt; 12 (or 13 on a
* leap year), the day of month is &lt; 1 or &gt; 30, an hour &lt; 0 or &gt; 23, a minute &lt; 0 &gt; 59 or chalakim &lt; 0 &gt;
* 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 &lt; 0 or &gt; 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 &lt; 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 &lt; 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 &lt; 1 or &gt; 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 &lt; 3761 is passed in. The same will happen if the year is 3761 and the month and day
* previously set are &lt; 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 &lt; 1 or &gt; 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