Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
package com.kosherjava.zmanim;

import java.math.BigDecimal;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import com.kosherjava.zmanim.util.AstronomicalCalculator;
import com.kosherjava.zmanim.util.GeoLocation;
import com.kosherjava.zmanim.util.TimeZoneUtils;
import com.kosherjava.zmanim.util.ZmanimFormatter;

/**
Expand Down Expand Up @@ -350,6 +354,7 @@ public Date getSunriseOffsetByDegrees(double offsetZenith) {
*/
public Date getSunsetOffsetByDegrees(double offsetZenith) {
double sunset = getUTCSunset(offsetZenith);
// System.out.println("Jsunset: " + sunset);
if (Double.isNaN(sunset)) {
return null;
} else {
Expand Down Expand Up @@ -626,32 +631,40 @@ protected Date getDateFromTime(double time, SolarEvent solarEvent) {
return null;
}
double calculatedTime = time;

Calendar adjustedCalendar = getAdjustedCalendar();

// Convert Calendar to java.time for accurate date extraction, especially for distant future dates
long milliseconds = adjustedCalendar.getTimeInMillis();
Instant instant = Instant.ofEpochMilli(milliseconds);
TimeZone timeZone = adjustedCalendar.getTimeZone();
ZoneId zoneId = timeZone.toZoneId();
ZonedDateTime adjustedZdt = instant.atZone(zoneId);

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.clear();// clear all fields
cal.set(Calendar.YEAR, adjustedCalendar.get(Calendar.YEAR));
cal.set(Calendar.MONTH, adjustedCalendar.get(Calendar.MONTH));
cal.set(Calendar.DAY_OF_MONTH, adjustedCalendar.get(Calendar.DAY_OF_MONTH));
cal.set(Calendar.YEAR, adjustedZdt.getYear());
cal.set(Calendar.MONTH, adjustedZdt.getMonthValue() - 1); // Calendar months are 0-based
cal.set(Calendar.DAY_OF_MONTH, adjustedZdt.getDayOfMonth());

int hours = (int) calculatedTime; // retain only the hours
calculatedTime -= hours;
int minutes = (int) (calculatedTime *= 60); // retain only the minutes
calculatedTime -= minutes;
calculatedTime -= minutes;
int seconds = (int) (calculatedTime *= 60); // retain only the seconds
calculatedTime -= seconds; // remaining milliseconds

// Check if a date transition has occurred, or is about to occur - this indicates the date of the event is
// actually not the target date, but the day prior or after
int localTimeHours = (int)getGeoLocation().getLongitude() / 15;
if (solarEvent == SolarEvent.SUNRISE && localTimeHours + hours > 18) {
cal.add(Calendar.DAY_OF_MONTH, -1);
cal = TimeZoneUtils.addDay(cal, -1);
} else if (solarEvent == SolarEvent.SUNSET && localTimeHours + hours < 6) {
cal.add(Calendar.DAY_OF_MONTH, 1);
cal = TimeZoneUtils.addDay(cal, 1);
} else if (solarEvent == SolarEvent.MIDNIGHT && localTimeHours + hours < 12) {
cal.add(Calendar.DAY_OF_MONTH, 1);
cal = TimeZoneUtils.addDay(cal, 1);
} else if (solarEvent == SolarEvent.NOON && localTimeHours + hours > 24) {
cal.add(Calendar.DAY_OF_MONTH, -1);
cal = TimeZoneUtils.addDay(cal, -1);
}

cal.set(Calendar.HOUR_OF_DAY, hours);
Expand Down Expand Up @@ -758,8 +771,9 @@ public Date getLocalMeanTime(double hours) {
if (hours < 0 || hours >= 24) {
throw new IllegalArgumentException("Hours must between 0 and 23.9999...");
}
return getTimeOffset(getDateFromTime(hours - getGeoLocation().getTimeZone().getRawOffset()
/ (double) HOUR_MILLIS, SolarEvent.SUNRISE), -getGeoLocation().getLocalMeanTimeOffset());
long timezoneOffsetMillis = TimeZoneUtils.getTimezoneOffsetAt(getCalendar());
return getTimeOffset(getDateFromTime(hours - timezoneOffsetMillis
/ (double) HOUR_MILLIS, SolarEvent.SUNRISE), -getGeoLocation().getLocalMeanTimeOffset(calendar));
}

/**
Expand All @@ -769,12 +783,11 @@ public Date getLocalMeanTime(double hours) {
* @return the adjusted Calendar
*/
private Calendar getAdjustedCalendar(){
int offset = getGeoLocation().getAntimeridianAdjustment();
int offset = getGeoLocation().getAntimeridianAdjustment(getCalendar());
if (offset == 0) {
return getCalendar();
}
Calendar adjustedCalendar = (Calendar) getCalendar().clone();
adjustedCalendar.add(Calendar.DAY_OF_MONTH, offset);
Calendar adjustedCalendar = TimeZoneUtils.addDay(getCalendar(), offset);
return adjustedCalendar;
}

Expand Down
20 changes: 12 additions & 8 deletions src/main/java/com/kosherjava/zmanim/ComplexZmanimCalendar.java
Original file line number Diff line number Diff line change
Expand Up @@ -3486,18 +3486,22 @@ public Date getSofZmanKidushLevanaBetweenMoldos(Date alos, Date tzais) {
*/
private Date getMoladBasedTime(Date moladBasedTime, Date alos, Date tzais, boolean techila) {
Date lastMidnight = getMidnightLastNight();
Date midnightTonight = getMidnightTonight();
if (!(moladBasedTime.before(lastMidnight) || moladBasedTime.after(midnightTonight))){
if (alos != null || tzais != null) {
if (techila && !(moladBasedTime.before(tzais) || moladBasedTime.after(alos))){
Date midnightTonight = getMidnightTonight();
if(moladBasedTime.before(lastMidnight) || moladBasedTime.after(midnightTonight)){ // Invalid time, bailout
return null;
} else if (alos == null || tzais == null){ // Not enough info to adjust
return moladBasedTime;
} else { // It's the daytime, get the next/prev night
if (moladBasedTime.after(alos) && moladBasedTime.before(tzais)) {
if (techila) {
return tzais;
} else {
return alos;
}
}
return moladBasedTime;
}
return null;
} else { // It's the night, the provided time is valid
return moladBasedTime;
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,7 @@ public Date getMoladAsDate() {
molad.getMoladHours(), molad.getMoladMinutes(), (int) moladSeconds);
cal.set(Calendar.MILLISECOND, (int) (1000 * (moladSeconds - (int) moladSeconds)));
// subtract local time difference of 20.94 minutes (20 minutes and 56.496 seconds) to get to Standard time
cal.add(Calendar.MILLISECOND, -1 * (int) geo.getLocalMeanTimeOffset());
cal.add(Calendar.MILLISECOND, -1 * (int) geo.getLocalMeanTimeOffset(cal));
return cal.getTime();
}

Expand Down
49 changes: 33 additions & 16 deletions src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

/**
* The JewishDate is the base calendar class, that supports maintenance of a {@link java.util.GregorianCalendar}
Expand Down Expand Up @@ -567,8 +568,8 @@ private static int getJewishMonthOfYear(int year, int month) {
* @param month
* the Jewish month to validate. It will reject a month &lt; 1 or &gt; 12 (or 13 on a leap year) .
* @param dayOfMonth
* the day of the Jewish month to validate. It will reject any value &lt; 1 or &gt; 30 TODO: check calling
* methods to see if there is any reason that the class can validate that 30 is invalid for some months.
* the day of the Jewish month to validate. It will reject any value &lt; 1 or &gt; the number of days in the month
* for that year.
* @param hours
* the hours (for <em>molad</em> calculations). It will reject an hour &lt; 0 or &gt; 23
* @param minutes
Expand All @@ -590,9 +591,12 @@ private static void validateJewishDate(int year, int month, int dayOfMonth, int
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.");

int maxDaysInMonth = getDaysInJewishMonth(month, year);

if (dayOfMonth < 1 || dayOfMonth > maxDaysInMonth) {
throw new IllegalArgumentException("The Jewish day of month can't be < 1 or > " + maxDaysInMonth + ". " + dayOfMonth
+ " is invalid for the month " + month + " of the year " + year + ".");
}
// reject dates prior to 18 Teves, 3761 (1/1/1 AD). This restriction can be relaxed if the date coding is
// changed/corrected
Expand Down Expand Up @@ -1180,7 +1184,12 @@ public void setJewishDate(int year, int month, int dayOfMonth, int hours, int mi
*/
public Calendar getGregorianCalendar() {
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
calendar.set(getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}

Expand Down Expand Up @@ -1292,28 +1301,36 @@ public void forward(int field, int amount) {
}

/**
* Forward the Jewish date by the number of months passed in.
* FIXME: Deal with forwarding a date such as 30 Nissan by a month. 30 Iyar does not exist. This should be dealt with similar to
* the way that the Java Calendar behaves (not that simple since there is a difference between add() or roll().
* Advances the Jewish date forward by the specified number of months.
* If the day doesn't exist in the target month (e.g., 30 Iyar), it adjusts to the last day of that month (29 Iyar).
*
* @throws IllegalArgumentException if the amount is less than 1
* @param amount the number of months to roll the month forward
* @param amount the number of months to advance (must be at least 1)
*/
private void forwardJewishMonth(int amount) {
if (amount < 1) {
throw new IllegalArgumentException("the amount of months to forward has to be greater than zero.");
}
int currentMonth = getJewishMonth();
int currentYear = getJewishYear();
int currentDay = getJewishDayOfMonth();
for (int i = 0; i < amount; i++) {
if (getJewishMonth() == ELUL) {
setJewishMonth(TISHREI);
setJewishYear(getJewishYear() + 1);
} else if ((! isJewishLeapYear() && getJewishMonth() == ADAR)
|| (isJewishLeapYear() && getJewishMonth() == ADAR_II)){
setJewishMonth(NISSAN);
boolean isLeapYear = JewishDate.isJewishLeapYear(currentYear);
if (currentMonth == ELUL) {
currentMonth = TISHREI;
currentYear = currentYear + 1;
} else if ((!isLeapYear && currentMonth == ADAR)
|| (isLeapYear && currentMonth == ADAR_II)){
currentMonth = NISSAN;
} else {
setJewishMonth(getJewishMonth() + 1);
currentMonth = currentMonth + 1;
}
}
int maxDaysInMonth = JewishDate.getDaysInJewishMonth(currentMonth, currentYear);
if (currentDay > maxDaysInMonth) {
currentDay = maxDaysInMonth;
}
setJewishDate(currentYear, currentMonth, currentDay);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import com.kosherjava.zmanim.util.TimeZoneUtils;

/**
* This class calculates the <a href="https://en.wikipedia.org/wiki/Jerusalem_Talmud">Talmud Yerusalmi</a> <a href=
Expand All @@ -31,7 +33,11 @@ public class YerushalmiYomiCalculator {
/**
* The start date of the first Daf Yomi Yerushalmi cycle of February 2, 1980 / 15 Shevat, 5740.
*/
private final static Calendar DAF_YOMI_START_DAY = new GregorianCalendar(1980, Calendar.FEBRUARY, 2);
private final static Calendar DAF_YOMI_START_DAY;
static {
DAF_YOMI_START_DAY = new GregorianCalendar(1980, Calendar.FEBRUARY, 2);
DAF_YOMI_START_DAY.setTimeZone(TimeZone.getTimeZone("UTC"));
}
/** The number of milliseconds in a day. */
private final static int DAY_MILIS = 1000 * 60 * 60 * 24;
/** The number of pages in the Talmud Yerushalmi.*/
Expand Down Expand Up @@ -64,8 +70,8 @@ public YerushalmiYomiCalculator() {
*/
public static Daf getDafYomiYerushalmi(JewishCalendar calendar) {

Calendar nextCycle = new GregorianCalendar();
Calendar prevCycle = new GregorianCalendar();
Calendar nextCycle = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
Calendar prevCycle = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
Calendar requested = calendar.getGregorianCalendar();
int masechta = 0;
Daf dafYomi = null;
Expand All @@ -83,17 +89,23 @@ public static Daf getDafYomiYerushalmi(JewishCalendar calendar) {
}

// Start to calculate current cycle. init the start day
prevCycle.setTime(DAF_YOMI_START_DAY.getTime());
nextCycle.setTime(DAF_YOMI_START_DAY.getTime());

// Move the nextCycle to the last day of the current cycle
nextCycle = TimeZoneUtils.addDay(nextCycle, WHOLE_SHAS_DAFS - 1);
nextCycle = TimeZoneUtils.addDay(nextCycle, getNumOfSpecialDays(prevCycle, nextCycle));

// Go cycle by cycle, until we get the next cycle
while (requested.after(nextCycle)) {
// Move the prevCycle from the 1st day of the current cycle to the 1st day of the next cycle
prevCycle.setTime(nextCycle.getTime());

// Adds the number of whole shas dafs. and the number of days that not have daf.
nextCycle.add(Calendar.DAY_OF_MONTH, WHOLE_SHAS_DAFS);
nextCycle.add(Calendar.DAY_OF_MONTH, getNumOfSpecialDays(prevCycle, nextCycle));
prevCycle = TimeZoneUtils.addDay(prevCycle, 1);

// Move the nextCycle from the last day of the current cycle to the last day of the next cycle
nextCycle = TimeZoneUtils.addDay(nextCycle, WHOLE_SHAS_DAFS);
nextCycle = TimeZoneUtils.addDay(nextCycle, getNumOfSpecialDays(prevCycle, nextCycle));
}

// Get the number of days from cycle start until request.
int dafNo = (int)(getDiffBetweenDays(prevCycle, requested));

Expand All @@ -110,6 +122,7 @@ public static Daf getDafYomiYerushalmi(JewishCalendar calendar) {
total -= i;
masechta++;
}


return dafYomi;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

/**
* This class calculates the Daf Yomi Bavli page (daf) for a given date. To calculate Daf Yomi Yerushalmi
Expand All @@ -30,19 +31,27 @@ public class YomiCalculator {
/**
* The start date of the first Daf Yomi Bavli cycle of September 11, 1923 / Rosh Hashana 5684.
*/
private static final Calendar dafYomiStartDay = new GregorianCalendar(1923, Calendar.SEPTEMBER, 11);
private static final Calendar DAF_YOMI_START_DAY;
static {
DAF_YOMI_START_DAY = new GregorianCalendar(1923, Calendar.SEPTEMBER, 11,0,0,0);
DAF_YOMI_START_DAY.setTimeZone(TimeZone.getTimeZone("UTC"));
}
/** The start date of the first Daf Yomi Bavli cycle in the Julian calendar. Used internally for calculations.*/
private static final int dafYomiJulianStartDay = getJulianDay(dafYomiStartDay);
private static final int DAF_YOMI_JULIAN_START_DAY = getJulianDay(DAF_YOMI_START_DAY);
/**
* The date that the pagination for the Daf Yomi <em>Maseches Shekalim</em> changed to use the commonly used Vilna
* Shas pagination from the no longer commonly available Zhitomir / Slavuta Shas used by Rabbi Meir Shapiro.
*/
private static final Calendar shekalimChangeDay = new GregorianCalendar(1975, Calendar.JUNE, 24);
private static final Calendar SHEKALIM_CHANGE_DAY;
static {
SHEKALIM_CHANGE_DAY = new GregorianCalendar(1975, Calendar.JUNE, 24,0,0,0);
SHEKALIM_CHANGE_DAY.setTimeZone(TimeZone.getTimeZone("UTC"));
}

/** The Julian date that the cycle for Shekalim changed.
* @see #getDafYomiBavli(JewishCalendar) for details.
*/
private static final int shekalimJulianChangeDay = getJulianDay(shekalimChangeDay);
private static final int SHEKALIM_JULIAN_CHANGE_DAY = getJulianDay(SHEKALIM_CHANGE_DAY);

/**
* Default constructor.
Expand Down Expand Up @@ -89,17 +98,17 @@ public static Daf getDafYomiBavli(JewishCalendar jewishCalendar) {
int julianDay = getJulianDay(calendar);
int cycleNo;
int dafNo;
if (calendar.before(dafYomiStartDay)) {
if (calendar.before(DAF_YOMI_START_DAY)) {
// TODO: should we return a null or throw an IllegalArgumentException?
throw new IllegalArgumentException(calendar + " is prior to organized Daf Yomi Bavli cycles that started on "
+ dafYomiStartDay);
+ DAF_YOMI_START_DAY);
}
if (calendar.equals(shekalimChangeDay) || calendar.after(shekalimChangeDay)) {
cycleNo = 8 + ((julianDay - shekalimJulianChangeDay) / 2711);
dafNo = ((julianDay - shekalimJulianChangeDay) % 2711);
if (calendar.equals(SHEKALIM_CHANGE_DAY) || calendar.after(SHEKALIM_CHANGE_DAY)) {
cycleNo = 8 + ((julianDay - SHEKALIM_JULIAN_CHANGE_DAY) / 2711);
dafNo = ((julianDay - SHEKALIM_JULIAN_CHANGE_DAY) % 2711);
} else {
cycleNo = 1 + ((julianDay - dafYomiJulianStartDay) / 2702);
dafNo = ((julianDay - dafYomiJulianStartDay) % 2702);
cycleNo = 1 + ((julianDay - DAF_YOMI_JULIAN_START_DAY) / 2702);
dafNo = ((julianDay - DAF_YOMI_JULIAN_START_DAY) % 2702);
}

int total = 0;
Expand Down
Loading
Loading