Skip to content

Commit ad91c0a

Browse files
authored
JewishCalendar - Add getTekufa() and getTekufaAsInstant()
Closes #279 Thank you @Elyahu41
1 parent 7a26356 commit ad91c0a

1 file changed

Lines changed: 91 additions & 19 deletions

File tree

‎src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java‎

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/*
22
* Zmanim Java API
3-
* Copyright (C) 2011 - 2026 Eliyahu Hershfeld
4-
* Copyright (C) September 2002 Avrom Finkelstien
5-
* Copyright (C) 2019 - 2022 Y Paritcher
6-
* Copyright (C) 2026 Moshe Dicker
3+
* Copyright © 2011 - 2026 Eliyahu Hershfeld
4+
* Copyright © September 2002 Avrom Finkelstien
5+
* Copyright © 2019 - 2022 Y Paritcher
6+
* Copyright © 2026 Moshe Dicker
77
*
88
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
99
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
@@ -381,7 +381,7 @@ public boolean isBirkasHachamah() {
381381
* night, and we push off the bracha to Wednesday morning resulting in the 172 used in the calculation.
382382
*/
383383
// Pure integer math prevents implicit promotion to double. 28 * 365.25 is exactly 10227 days.
384-
// Using "return elapsedDays % (28 * 365.25) == 172" would break with an integer overflow if Jewish year >= 5,879,101 😀
384+
// Using "return elapsedDays % (28 * 365.25) == 172" would break with an integer overflow in Jewish year >= 5,879,101 😀
385385
return elapsedDays % 10227 == 172; // 28 years of 365.25 days + the offset from molad tohu mentioned above
386386
}
387387

@@ -612,7 +612,7 @@ public synchronized int getYomTovIndex() {
612612
if (day >= 17 && day <= 20 || day == 16) {
613613
return CHOL_HAMOED_PESACH;
614614
}
615-
if (day == 22 || day == 23 && !inIsrael) {
615+
if ((day == 22 && inIsrael) || (day == 23 && !inIsrael)) {
616616
return ISRU_CHAG;
617617
}
618618
if (isUseModernHolidays()
@@ -649,12 +649,12 @@ public synchronized int getYomTovIndex() {
649649
if (day == 5) {
650650
return EREV_SHAVUOS;
651651
}
652-
if (day == 6 || (day == 7 && !inIsrael)) {
653-
return SHAVUOS;
654-
}
655-
if (day == 7 || day == 8 && !inIsrael) {
656-
return ISRU_CHAG;
657-
}
652+
if (day == 6 || (day == 7 && !inIsrael)) {
653+
return SHAVUOS;
654+
}
655+
if ((day == 7 && inIsrael) || (day == 8 && !inIsrael)) {
656+
return ISRU_CHAG;
657+
}
658658
break;
659659
case TAMMUZ:
660660
// push off the fast day if it falls on Shabbos
@@ -710,14 +710,11 @@ public synchronized int getYomTovIndex() {
710710
if (day == 23 && !inIsrael) {
711711
return SIMCHAS_TORAH;
712712
}
713-
if (day == 23 || day == 24 && !inIsrael) {
713+
if ((day == 23 && inIsrael) || (day == 24 && !inIsrael)) {
714714
return ISRU_CHAG;
715715
}
716716
break;
717717
case KISLEV: // no yomtov in CHESHVAN
718-
// if (day == 24) {
719-
// return EREV_CHANUKAH;
720-
// } else
721718
if (day >= 25) {
722719
return CHANUKAH;
723720
}
@@ -1229,9 +1226,9 @@ public Instant getMoladAsInstant() {
12291226
JewishDate molad = getMolad();
12301227

12311228
// Standard time offset for Jerusalem: GMT+2
1232-
// The raw molad Date (point in time) must be generated using standard time. Using "Asia/Jerusalem" timezone will
1233-
// result in the time being incorrectly off by an hour in the summer due to DST. Proper adjustment for the actual
1234-
// time in DST will be done by the date formatter class used to display the Date.
1229+
// The raw point in time must be generated using standard time. Using "Asia/Jerusalem" timezone will result in the time
1230+
// being incorrectly off by an hour in the summer due to DST. Proper adjustment for the actual time in DST will be done
1231+
// by the formatter class used to display the Instant.
12351232
ZoneId jerusalemStandardOffset = ZoneId.of("GMT+2");
12361233

12371234
double moladSeconds = molad.getMoladChalakim() * 10.0 / 3.0; // Compute molad seconds from chalakim
@@ -1370,6 +1367,8 @@ public Daf getDafYomiYerushalmi() {
13701367
* @see com.kosherjava.zmanim.hebrewcalendar.TefilaRules#isVeseinTalUmatarStartDate(JewishCalendar)
13711368
* @see com.kosherjava.zmanim.hebrewcalendar.TefilaRules#isVeseinTalUmatarStartingTonight(JewishCalendar)
13721369
* @see com.kosherjava.zmanim.hebrewcalendar.TefilaRules#isYaalehVeyavoRecited(JewishCalendar)
1370+
* @see #getTekufa()
1371+
* @see #getTekufaAsInstant(boolean)
13731372
*/
13741373
public int getTekufasTishreiElapsedDays() {
13751374
// Days since Rosh Hashana year 1. Add 1/2 day as the first tekufas tishrei was 9 hours into the day. This allows all
@@ -1380,6 +1379,79 @@ public int getTekufasTishreiElapsedDays() {
13801379
return (int) Math.floor(days - solar);
13811380
}
13821381

1382+
/**
1383+
* Calculates the hours for when the <a href="https://en.wikipedia.org/wiki/Tekufah"><em>Tekufa</em></a> (season) changes.
1384+
* There are 4 <em>tekufos</em> a year: Nissan/Spring, Tammuz/Summer, Tishri/Fall, and Teves/Winter. This calculation is
1385+
* according to <a href="https://en.wikipedia.org/wiki/Samuel_of_Nehardea">Shmuel</a> in <a href=
1386+
* "https://hebrewbooks.org/shas.aspx?mesechta=3&daf=56">Eruvin 56a</a>, which is a more rounded up version of <a href=
1387+
* "https://en.wikipedia.org/wiki/Adda_bar_Ahavah">Rav Adda's</a> calculation. The <a href=
1388+
* "https://en.wikipedia.org/wiki/Moses_Isserles">Rama</a> writes in <a href=
1389+
* "https://hebrewbooks.org/pdfpager.aspx?req=67715&st=&pgnum=218">Yoreh De'ah 116:5</a> that one should not drink water during
1390+
* the <em>tekufa</em> change. <a href="https://en.wikipedia.org/wiki/Ovadia_Yosef">Rabbi Ovadia Yosef</a> (Halichot Olam,
1391+
* Chelek 7, Page 183) recommends to abstain from drinking water for a range of 1 hour (30 minutes before and after the
1392+
* <em>tekufa</em> event).
1393+
*
1394+
* @return the number of hours into the Hebrew day that the <em>tekufa</em> (season) change takes place as a {code Double}, or
1395+
* {@code null} if the <em>tekufa</em> does not occur on current day. For example: 19.5 would mean that the
1396+
* <em>tekufa</em> occurs 19 hours and a half into the Hebrew date, or 13 and a half hours (-6 hours) into the Gregorian
1397+
* date.
1398+
*/
1399+
private Double getTekufa() {
1400+
double INITIAL_TEKUFA_OFFSET = 12.625; // the number of days Tekufas Tishrei occurs before JEWISH_EPOCH
1401+
1402+
double days = getJewishCalendarElapsedDays(getJewishYear()) + getDaysSinceStartOfJewishYear() + INITIAL_TEKUFA_OFFSET - 1; // total days since first Tekufas Tishrei event
1403+
1404+
double solarDaysElapsed = days % 365.25; // total days elapsed since the start of solar year
1405+
double tekufaDaysElapsed = solarDaysElapsed % 91.3125; // the number of days that have passed since a tekufa event
1406+
if (tekufaDaysElapsed > 0 && tekufaDaysElapsed <= 1) { // if the tekufa happens in the upcoming 24 hours
1407+
return ((1.0 - tekufaDaysElapsed) * 24.0) % 24;// rationalize the tekufa event to number of hours since start of jewish day
1408+
} else {
1409+
return null;
1410+
}
1411+
}
1412+
1413+
/**
1414+
* Returns an {@code Instant} if the current day has a <a href="https://en.wikipedia.org/wiki/Tekufah"><em>Tekufa</em></a>
1415+
* (season) change. The {@code Instant} will contain the time that the <em>tekufa</em> (season) is arriving. If this method is
1416+
* called on a day without a <em>tekufa</em> change, it will return a {@code null}. The default implementation of this method
1417+
* will return the <em>tekufa</em> change according to the calculations of the <a href="https://zmanim.online/">Luach Itim
1418+
* Lebinah</a> following <a href="https://en.wikipedia.org/wiki/Yechiel_Michel_Tucazinsky">Rabbi Yechiel Michel Tucazinsky</a>.
1419+
* However, there is also the opinion of Rabbi Yonah Boron, who calculates the <em>tekufa</em> based on Local Mean Time (LMT)
1420+
* in Israel which causes a 21-minute difference. There is a third opinion as well to use seasonal midday but that is not a
1421+
* generally followed opinion, so it has not been implemented.
1422+
* @param useLocalMeanTime if true, removes ~21 minutes from the time of the <em>tekufa</em> calculated by Rabbi Yechiel Michel
1423+
* Tucazinsky, and will follow the opinion of Rabbi Yonah Boron.
1424+
* @return an Instant with the time that the <em>tekufa</em> (season) changes or a null on a day with no <em>tekufa</em> change.
1425+
* @see #getTekufa()
1426+
*/
1427+
public Instant getTekufaAsInstant(boolean useLocalMeanTime) {
1428+
Double hours = getTekufa();
1429+
if (hours == null) {
1430+
return null;
1431+
}
1432+
// The tekufa Date must be generated using standard time. Using "Asia/Jerusalem" timezone will result in the time being
1433+
// incorrectly off by an hour in the summer due to DST. Proper adjustment for the actual time in DST will be done by the
1434+
// formatter class used to display the Instant.
1435+
ZoneId yerushalayimStandardTZ = ZoneId.of("GMT+2");
1436+
1437+
hours = hours - 6; // minus 6 hours because the hebrew date starts at sunset which is at 6PM
1438+
int minutes = (int) ((hours - hours.intValue()) * 60);
1439+
1440+
LocalTime time = LocalTime.of(hours.intValue(), minutes);
1441+
ZonedDateTime tekufaZDT = ZonedDateTime.of(getLocalDate(), time, yerushalayimStandardTZ);
1442+
1443+
// Har Habayis at a longitude of 35.2354 offset vs longitude 35 in standard time, so we subtract the time difference
1444+
// of 20.94 minutes (20 minutes and 56 seconds and 496 millis) to get to Standard time from local mean time
1445+
if (useLocalMeanTime) {
1446+
Duration jerusalemStandardTimeOffset = Duration.ofMinutes(20)
1447+
.plusSeconds(56)
1448+
.plusMillis(496);
1449+
return tekufaZDT.minus(jerusalemStandardTimeOffset).toInstant();
1450+
} else {
1451+
return tekufaZDT.toInstant();
1452+
}
1453+
}
1454+
13831455
/**
13841456
* Returns true if the current day is <em>Isru Chag</em>. The method returns true for the day following <em>Pesach</em>
13851457
* <em>Shavuos</em> and <em>Succos</em>. It utilizes {@see #getInIsrael()} to return the proper date.

0 commit comments

Comments
 (0)