1717 */
1818package com .kosherjava .zmanim .hebrewcalendar ;
1919
20- import com . kosherjava . zmanim . util . GeoLocation ;
21-
20+ import java . time . Duration ;
21+ import java . time . Instant ;
2222import java .time .LocalDate ;
23+ import java .time .ZoneOffset ;
24+ import java .time .ZonedDateTime ;
25+ import java .time .temporal .ChronoUnit ;
2326import java .util .Calendar ;
24- import java .util .Date ;
25- import java .util .TimeZone ;
2627
2728/**
2829 * The JewishCalendar extends the JewishDate class and adds calendar methods.
4243 * @see java.util.Calendar
4344 * @author © Y. Paritcher 2019 - 2022
4445 * @author © Avrom Finkelstien 2002
45- * @author © Eliyahu Hershfeld 2011 - 2024
46+ * @author © Eliyahu Hershfeld 2011 - 2026
4647 */
4748public class JewishCalendar extends JewishDate {
4849 /** The 14th day of Nissan, the day before Pesach (Passover).*/
@@ -256,21 +257,21 @@ public JewishCalendar() {
256257 /**
257258 * A constructor that initializes the date to the {@link java.util.Date Date} parameter.
258259 *
259- * @param date
260- * the <code>Date </code> to set the calendar to
260+ * @param instant
261+ * the <code>Instant </code> to set the calendar to
261262 */
262- public JewishCalendar (Date date ) {
263- super (date );
263+ public JewishCalendar (Instant instant ) {
264+ super (instant );
264265 }
265266
266267 /**
267268 * A constructor that initializes the date to the {@link java.util.Calendar Calendar} parameter.
268269 *
269- * @param calendar
270- * the <code>Calendar </code> to set the calendar to
270+ * @param zonedDateTime
271+ * the <code>ZonedDateTime </code> to set the calendar to
271272 */
272- public JewishCalendar (Calendar calendar ) {
273- super (calendar );
273+ public JewishCalendar (ZonedDateTime zonedDateTime ) {
274+ super (zonedDateTime );
274275 }
275276
276277 /**
@@ -1230,27 +1231,41 @@ public boolean isTishaBav() {
12301231 *
12311232 * @return the Date representing the moment of the <em>molad</em> in Yerushalayim standard time (GMT + 2)
12321233 */
1233- public Date getMoladAsDate () {
1234- JewishDate molad = getMolad ();
1235- String locationName = "Jerusalem, Israel" ;
1234+ public Instant getMoladAsInstant () {
1235+ JewishDate molad = getMolad ();
1236+
1237+ // Har Habayis coordinates
1238+ //double latitude = 31.778;
1239+ double longitude = 35.2354 ;
1240+
1241+ // Standard time offset for Jerusalem: GMT+2
1242+ ZoneOffset jerusalemStandardOffset = ZoneOffset .ofHours (2 );
1243+
1244+ // Compute molad seconds from chalakim
1245+ double moladSeconds = molad .getMoladChalakim () * 10.0 / 3.0 ;
1246+ int seconds = (int ) moladSeconds ;
1247+ int millis = (int ) ((moladSeconds - seconds ) * 1000 );
1248+
1249+ // Construct ZonedDateTime in standard time (GMT+2)
1250+ ZonedDateTime moladZdt = ZonedDateTime .of (
1251+ molad .getGregorianYear (),
1252+ molad .getGregorianMonth () +1 , // 1-based FIXME
1253+ molad .getGregorianDayOfMonth (),
1254+ molad .getMoladHours (),
1255+ molad .getMoladMinutes (),
1256+ seconds ,
1257+ millis * 1_000_000 , // nanos
1258+ jerusalemStandardOffset
1259+ );
12361260
1237- double latitude = 31.778 ; // Har Habayis latitude
1238- double longitude = 35.2354 ; // Har Habayis longitude
1261+ // Compute local mean time offset in milliseconds (example: 20.94 minutes = 1256400 ms)
1262+ long localMeanOffsetMillis = ( long ) (( longitude - 35.0 ) * 4 * 60 * 1000 );
12391263
1240- // The raw molad Date (point in time) must be generated using standard time. Using "Asia/Jerusalem" timezone will result in the time
1241- // being incorrectly off by an hour in the summer due to DST. Proper adjustment for the actual time in DST will be done by the date
1242- // formatter class used to display the Date.
1243- TimeZone yerushalayimStandardTZ = TimeZone .getTimeZone ("GMT+2" );
1244- GeoLocation geo = new GeoLocation (locationName , latitude , longitude , yerushalayimStandardTZ );
1245- Calendar cal = Calendar .getInstance (geo .getTimeZone ());
1246- cal .clear ();
1247- double moladSeconds = molad .getMoladChalakim () * 10 / (double ) 3 ;
1248- cal .set (molad .getGregorianYear (), molad .getGregorianMonth (), molad .getGregorianDayOfMonth (),
1249- molad .getMoladHours (), molad .getMoladMinutes (), (int ) moladSeconds );
1250- cal .set (Calendar .MILLISECOND , (int ) (1000 * (moladSeconds - (int ) moladSeconds )));
1251- // subtract local time difference of 20.94 minutes (20 minutes and 56.496 seconds) to get to Standard time
1252- cal .add (Calendar .MILLISECOND , -1 * (int ) geo .getLocalMeanTimeOffset ());
1253- return cal .getTime ();
1264+ // Apply offset using ChronoUnit.MILLIS
1265+ moladZdt = moladZdt .minus (localMeanOffsetMillis , ChronoUnit .MILLIS );
1266+
1267+ // Return precise Instant
1268+ return moladZdt .toInstant ();
12541269 }
12551270
12561271 /**
@@ -1263,12 +1278,8 @@ public Date getMoladAsDate() {
12631278 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana3Days()
12641279 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana3Days(Date, Date)
12651280 */
1266- public Date getTchilasZmanKidushLevana3Days () {
1267- Date molad = getMoladAsDate ();
1268- Calendar cal = Calendar .getInstance ();
1269- cal .setTime (molad );
1270- cal .add (Calendar .HOUR , 72 ); // 3 days after the molad
1271- return cal .getTime ();
1281+ public Instant getTchilasZmanKidushLevana3Days () {
1282+ return getMoladAsInstant ().plus (Duration .ofHours (72 )); // 3 days after the molad
12721283 }
12731284
12741285 /**
@@ -1283,12 +1294,8 @@ public Date getTchilasZmanKidushLevana3Days() {
12831294 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana7Days()
12841295 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getTchilasZmanKidushLevana7Days(Date, Date)
12851296 */
1286- public Date getTchilasZmanKidushLevana7Days () {
1287- Date molad = getMoladAsDate ();
1288- Calendar cal = Calendar .getInstance ();
1289- cal .setTime (molad );
1290- cal .add (Calendar .HOUR , 168 ); // 7 days after the molad
1291- return cal .getTime ();
1297+ public Instant getTchilasZmanKidushLevana7Days () {
1298+ return getMoladAsInstant ().plus (Duration .ofHours (168 )); // 7 days after the molad
12921299 }
12931300
12941301 /**
@@ -1306,18 +1313,18 @@ public Date getTchilasZmanKidushLevana7Days() {
13061313 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevanaBetweenMoldos()
13071314 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevanaBetweenMoldos(Date, Date)
13081315 */
1309- public Date getSofZmanKidushLevanaBetweenMoldos () {
1310- Date molad = getMoladAsDate ();
1311- Calendar cal = Calendar . getInstance ();
1312- cal . setTime ( molad );
1313- // add half the time between molad and molad (half of 29 days, 12 hours and 793 chalakim (44 minutes, 3.3
1314- // seconds), or 14 days, 18 hours, 22 minutes and 666 milliseconds). Add it as hours, not days, to avoid
1315- // DST/ST crossover issues.
1316- cal . add ( Calendar . HOUR , ( 24 * 14 ) + 18 );
1317- cal . add ( Calendar . MINUTE , 22 );
1318- cal . add ( Calendar . SECOND , 1 );
1319- cal . add ( Calendar . MILLISECOND , 666 );
1320- return cal . getTime ( );
1316+ public Instant getSofZmanKidushLevanaBetweenMoldos () {
1317+ Instant molad = getMoladAsInstant ();
1318+
1319+ // Duration for half of the lunar month:
1320+ // 14 days, 18 hours, 22 minutes, 1 second, 666 milliseconds
1321+ Duration halfLunarMonth = Duration . ofDays ( 14 )
1322+ . plusHours ( 18 )
1323+ . plusMinutes ( 22 )
1324+ . plusSeconds ( 1 )
1325+ . plusMillis ( 666 );
1326+
1327+ return molad . plus ( halfLunarMonth );
13211328 }
13221329
13231330 /**
@@ -1337,12 +1344,8 @@ public Date getSofZmanKidushLevanaBetweenMoldos() {
13371344 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevana15Days()
13381345 * @see com.kosherjava.zmanim.ComprehensiveZmanimCalendar#getSofZmanKidushLevana15Days(Date, Date)
13391346 */
1340- public Date getSofZmanKidushLevana15Days () {
1341- Date molad = getMoladAsDate ();
1342- Calendar cal = Calendar .getInstance ();
1343- cal .setTime (molad );
1344- cal .add (Calendar .HOUR , 24 * 15 ); //15 days after the molad. Add it as hours, not days, to avoid DST/ST crossover issues.
1345- return cal .getTime ();
1347+ public Instant getSofZmanKidushLevana15Days () {
1348+ return getMoladAsInstant ().plus (Duration .ofHours (24 * 15 ));
13461349 }
13471350
13481351 /**
@@ -1381,9 +1384,9 @@ public Daf getDafYomiYerushalmi() {
13811384 *
13821385 * @return the number of elapsed days since <em>tekufas Tishrei</em>.
13831386 *
1384- * @see #isVeseinTalUmatarStartDate()
1385- * @see #isVeseinTalUmatarStartingTonight()
1386- * @see #isVeseinTalUmatarRecited( )
1387+ * @see com.kosherjava.zmanim.hebrewcalendar.TefilaRules #isVeseinTalUmatarStartDate(JewishCalendar )
1388+ * @see com.kosherjava.zmanim.hebrewcalendar.TefilaRules #isVeseinTalUmatarStartingTonight(JewishCalendar )
1389+ * @see com.kosherjava.zmanim.hebrewcalendar.TefilaRules#isYaalehVeyavoRecited(JewishCalendar )
13871390 */
13881391 public int getTekufasTishreiElapsedDays () {
13891392 // Days since Rosh Hashana year 1. Add 1/2 day as the first tekufas tishrei was 9 hours into the day. This allows all
@@ -1393,185 +1396,6 @@ public int getTekufasTishreiElapsedDays() {
13931396 double solar = (getJewishYear () - 1 ) * 365.25 ;
13941397 return (int ) Math .floor (days - solar );
13951398 }
1396-
1397- /**
1398- * Returns if it is the Jewish day (starting the evening before) to start reciting <em>Vesein Tal Umatar
1399- * Livracha</em> (<em>Sheailas Geshamim</em>). In Israel this is the 7th day of <em>Marcheshvan</em>. Outside
1400- * Israel recitation starts on the evening of December 4th (or 5th if it is the year before a civil leap year)
1401- * in the 21st century and shifts a day forward every century not evenly divisible by 400. This method will
1402- * return true if <em>vesein tal umatar</em> on the current Jewish date that starts on the previous night, so
1403- * Dec 5/6 will be returned by this method in the 21st century. <em>vesein tal umatar</em> is not recited on
1404- * <em>Shabbos</em> and the start date will be delayed a day when the start day is on a <em>Shabbos</em> (this
1405- * can only occur out of Israel).
1406- *
1407- * @deprecated Use {@link TefilaRules#isVeseinTalUmatarStartDate(JewishCalendar)} instead. This method will be
1408- * removed in the v3.0 release.
1409- *
1410- * @return true if it is the first Jewish day (starting the prior evening of reciting <em>Vesein Tal Umatar
1411- * Livracha</em> (<em>Sheailas Geshamim</em>)).
1412- *
1413- * @see #isVeseinTalUmatarStartingTonight()
1414- * @see #isVeseinTalUmatarRecited()
1415- */
1416- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1417- public boolean isVeseinTalUmatarStartDate () {
1418- if (inIsrael ) {
1419- // The 7th Cheshvan can't occur on Shabbos, so always return true for 7 Cheshvan
1420- return getJewishMonth () == CHESHVAN && getJewishDayOfMonth () == 7 ;
1421- } else {
1422- if (getDayOfWeek () == Calendar .SATURDAY ) { //Not recited on Friday night
1423- return false ;
1424- }
1425- if (getDayOfWeek () == Calendar .SUNDAY ) { // When starting on Sunday, it can be the start date or delayed from Shabbos
1426- return getTekufasTishreiElapsedDays () == 48 || getTekufasTishreiElapsedDays () == 47 ;
1427- } else {
1428- return getTekufasTishreiElapsedDays () == 47 ;
1429- }
1430- }
1431- }
1432-
1433- /**
1434- * Returns true if tonight is the first night to start reciting <em>Vesein Tal Umatar Livracha</em> (
1435- * <em>Sheailas Geshamim</em>). In Israel this is the 7th day of <em>Marcheshvan</em> (so the 6th will return
1436- * true). Outside Israel recitation starts on the evening of December 4th (or 5th if it is the year before a
1437- * civil leap year) in the 21st century and shifts a day forward every century not evenly divisible by 400.
1438- * <em>Vesein tal umatar</em> is not recited on <em>Shabbos</em> and the start date will be delayed a day when
1439- * the start day is on a <em>Shabbos</em> (this can only occur out of Israel).
1440- *
1441- * @deprecated Use {@link TefilaRules#isVeseinTalUmatarStartingTonight(JewishCalendar)} instead. This method
1442- * will be removed in the v3.0 release.
1443- *
1444- * @return true if it is the first Jewish day (starting the prior evening of reciting <em>Vesein Tal Umatar
1445- * Livracha</em> (<em>Sheailas Geshamim</em>)).
1446- *
1447- * @see #isVeseinTalUmatarStartDate()
1448- * @see #isVeseinTalUmatarRecited()
1449- */
1450- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1451- public boolean isVeseinTalUmatarStartingTonight () {
1452- if (inIsrael ) {
1453- // The 7th Cheshvan can't occur on Shabbos, so always return true for 6 Cheshvan
1454- return getJewishMonth () == CHESHVAN && getJewishDayOfMonth () == 6 ;
1455- } else {
1456- if (getDayOfWeek () == Calendar .FRIDAY ) { //Not recited on Friday night
1457- return false ;
1458- }
1459- if (getDayOfWeek () == Calendar .SATURDAY ) { // When starting on motzai Shabbos, it can be the start date or delayed from Friday night
1460- return getTekufasTishreiElapsedDays () == 47 || getTekufasTishreiElapsedDays () == 46 ;
1461- } else {
1462- return getTekufasTishreiElapsedDays () == 46 ;
1463- }
1464- }
1465- }
1466-
1467- /**
1468- * Returns if <em>Vesein Tal Umatar Livracha</em> (<em>Sheailas Geshamim</em>) is recited. This will return
1469- * true for the entire season, even on <em>Shabbos</em> when it is not recited.
1470- *
1471- * @deprecated Use {@link TefilaRules#isVeseinTalUmatarRecited(JewishCalendar)} instead. This method will
1472- * be removed in the v3.0 release.
1473- *
1474- * @return true if <em>Vesein Tal Umatar Livracha</em> (<em>Sheailas Geshamim</em>) is recited.
1475- *
1476- * @see #isVeseinTalUmatarStartDate()
1477- * @see #isVeseinTalUmatarStartingTonight()
1478- */
1479- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1480- public boolean isVeseinTalUmatarRecited () {
1481- if (getJewishMonth () == NISSAN && getJewishDayOfMonth () < 15 ) {
1482- return true ;
1483- }
1484- if (getJewishMonth () < CHESHVAN ) {
1485- return false ;
1486- }
1487- if (inIsrael ) {
1488- return getJewishMonth () != CHESHVAN || getJewishDayOfMonth () >= 7 ;
1489- } else {
1490- return getTekufasTishreiElapsedDays () >= 47 ;
1491- }
1492- }
1493-
1494- /**
1495- * Returns if <em>Vesein Beracha</em> is recited. It is recited from 15 <em>Nissan</em> to the point that {@link
1496- * #isVeseinTalUmatarRecited() <em>vesein tal umatar</em> is recited}.
1497- *
1498- * @deprecated Use {@link TefilaRules#isVeseinBerachaRecited(JewishCalendar)} instead. This method will be
1499- * removed in the v3.0 release.
1500- *
1501- * @return true if <em>Vesein Beracha</em> is recited.
1502- *
1503- * @see #isVeseinTalUmatarRecited()
1504- */
1505- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1506- public boolean isVeseinBerachaRecited () {
1507- return !isVeseinTalUmatarRecited ();
1508- }
1509-
1510- /**
1511- * Returns if the date is the start date for reciting <em>Mashiv Haruach Umorid Hageshem</em>. The date is 22 <em>Tishrei</em>.
1512- *
1513- * @deprecated Use {@link TefilaRules#isMashivHaruachStartDate(JewishCalendar)} instead. This method will be
1514- * removed in the v3.0 release.
1515- *
1516- * @return true if the date is the start date for reciting <em>Mashiv Haruach Umorid Hageshem</em>.
1517- *
1518- * @see #isMashivHaruachEndDate()
1519- * @see #isMashivHaruachRecited()
1520- */
1521- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1522- public boolean isMashivHaruachStartDate () {
1523- return getJewishMonth () == TISHREI && getJewishDayOfMonth () == 22 ;
1524- }
1525-
1526- /**
1527- * Returns if the date is the end date for reciting <em>Mashiv Haruach Umorid Hageshem</em>. The date is 15 <em>Nissan</em>.
1528- *
1529- * @deprecated Use {@link TefilaRules#isMashivHaruachEndDate(JewishCalendar)} instead. This method will be
1530- * removed in the v3.0 release.
1531- *
1532- * @return true if the date is the end date for reciting <em>Mashiv Haruach Umorid Hageshem</em>.
1533- *
1534- * @see #isMashivHaruachStartDate()
1535- * @see #isMashivHaruachRecited()
1536- */
1537- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1538- public boolean isMashivHaruachEndDate () {
1539- return getJewishMonth () == NISSAN && getJewishDayOfMonth () == 15 ;
1540- }
1541-
1542- /**
1543- * Returns if <em>Mashiv Haruach Umorid Hageshem</em> is recited. This period starts on 22 <em>Tishrei</em> and ends
1544- * on the 15th day of <em>Nissan</em>.
1545- *
1546- * @deprecated Use {@link TefilaRules#isMashivHaruachRecited(JewishCalendar)} instead. This method will be
1547- * removed in the v3.0 release.
1548- *
1549- * @return true if <em>Mashiv Haruach Umorid Hageshem</em> is recited.
1550- *
1551- * @see #isMashivHaruachStartDate()
1552- * @see #isMashivHaruachEndDate()
1553- */
1554- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1555- public boolean isMashivHaruachRecited () {
1556- JewishDate startDate = new JewishDate (getJewishYear (), TISHREI , 22 );
1557- JewishDate endDate = new JewishDate (getJewishYear (), NISSAN , 15 );
1558- return compareTo (startDate ) > 0 && compareTo (endDate ) < 0 ;
1559- }
1560-
1561- /**
1562- * Returns if <em>Morid Hatal</em> (or the lack of reciting <em>Mashiv Haruach</em> following <em>nussach Ashkenaz</em>) is recited.
1563- * This period starts on 22 <em>Tishrei</em> and ends on the 15th day of
1564- * <em>Nissan</em>.
1565- *
1566- * @deprecated Use {@link TefilaRules#isMoridHatalRecited(JewishCalendar)} instead. This method will be
1567- * removed in the v3.0 release.
1568- *
1569- * @return true if <em>Morid Hatal</em> (or the lack of reciting <em>Mashiv Haruach</em> following <em>nussach Ashkenaz</em>) is recited.
1570- */
1571- @ Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
1572- public boolean isMoridHatalRecited () {
1573- return !isMashivHaruachRecited () || isMashivHaruachStartDate () || isMashivHaruachEndDate ();
1574- }
15751399
15761400 /**
15771401 * Returns true if the current day is <em>Isru Chag</em>. The method returns true for the day following <em>Pesach</em>
0 commit comments