diff --git a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java index 04f829d6..f891f35f 100644 --- a/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/AstronomicalCalendar.java @@ -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; /** @@ -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 { @@ -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); @@ -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)); } /** @@ -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; } diff --git a/src/main/java/com/kosherjava/zmanim/ComplexZmanimCalendar.java b/src/main/java/com/kosherjava/zmanim/ComplexZmanimCalendar.java index 4a0e2961..b7f17377 100644 --- a/src/main/java/com/kosherjava/zmanim/ComplexZmanimCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/ComplexZmanimCalendar.java @@ -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; + } + } } /** diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java index acc987f2..bc612758 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishCalendar.java @@ -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(); } diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java index eb47d309..71b68ec2 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/JewishDate.java @@ -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} @@ -567,8 +568,8 @@ private static int getJewishMonthOfYear(int year, int month) { * @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. + * the day of the Jewish month to validate. It will reject any value < 1 or > the number of days in the month + * for that year. * @param hours * the hours (for molad calculations). It will reject an hour < 0 or > 23 * @param minutes @@ -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 @@ -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; } @@ -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); } /** diff --git a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java index a5603e9b..d330476e 100644 --- a/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java +++ b/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YerushalmiYomiCalculator.java @@ -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 Talmud Yerusalmi Maseches Shekalim 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. @@ -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; diff --git a/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java b/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java index b2888a33..b3c53b4c 100644 --- a/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java +++ b/src/main/java/com/kosherjava/zmanim/util/GeoLocation.java @@ -15,6 +15,7 @@ */ package com.kosherjava.zmanim.util; +import java.util.Calendar; import java.util.Objects; import java.util.TimeZone; @@ -329,15 +330,17 @@ public void setTimeZone(TimeZone timeZone) { * so a user who is 1° west of this will have noon at 4 minutes after standard time noon, and conversely, a user * who is 1° east of the 15° longitude will have noon at 11:56 AM. Lakewood, N.J., whose longitude is * -74.222, is 0.778 away from the closest multiple of 15 at -75°. This is multiplied by 4 to yield 3 minutes - * and 10 seconds earlier than standard time. The offset returned does not account for the Daylight saving time offset since this class is - * unaware of dates. + * and 10 seconds earlier than standard time. The offset returned uses the actual timezone offset at the specific + * date/time from the Calendar, accounting for Daylight + * saving time. * - * @return the offset in milliseconds not accounting for Daylight saving time. A positive value will be returned - * East of the 15° timezone line, and a negative value West of it. + * @param calendar the Calendar containing the date/time to calculate the offset for + * @return the offset in milliseconds. A positive value will be returned East of the 15° timezone line, and a + * negative value West of it. */ - public long getLocalMeanTimeOffset() { - return (long) (getLongitude() * 4 * MINUTE_MILLIS - getTimeZone().getRawOffset()); + public long getLocalMeanTimeOffset(Calendar calendar) { + long timezoneOffsetMillis = TimeZoneUtils.getTimezoneOffsetAt(calendar); + return (long) (getLongitude() * 4 * MINUTE_MILLIS - timezoneOffsetMillis); } /** @@ -355,10 +358,11 @@ public long getLocalMeanTimeOffset() { * UTC time, the local DST offset of UTC+14:00 should be applied * to bring the date back to 2018-02-03. * + * @param calendar the Calendar containing the date/time to calculate the adjustment for * @return the number of days to adjust the date This will typically be 0 unless the date crosses the antimeridian */ - public int getAntimeridianAdjustment() { - double localHoursOffset = getLocalMeanTimeOffset() / (double)HOUR_MILLIS; + public int getAntimeridianAdjustment(Calendar calendar) { + double localHoursOffset = getLocalMeanTimeOffset(calendar) / (double)HOUR_MILLIS; if (localHoursOffset >= 20){// if the offset is 20 hours or more in the future (never expected anywhere other // than a location using a timezone across the antimeridian to the east such as Samoa) @@ -565,6 +569,10 @@ public double getRhumbLineDistance(GeoLocation location) { * @return The XML formatted String. */ public String toXML() { + Calendar cal = Calendar.getInstance(getTimeZone()); + long gmtOffsetMillis = TimeZoneUtils.getTimezoneOffsetAt(cal); + long dstOffsetMillis = getTimeZone().getDSTSavings(); + return "\n" + "\t" + getLocationName() + "\n" + "\t" + getLatitude() + "\n" + @@ -572,9 +580,9 @@ public String toXML() { "\t" + getElevation() + " Meters" + "\n" + "\t" + getTimeZone().getID() + "\n" + "\t" + getTimeZone().getDisplayName() + "\n" + - "\t" + getTimeZone().getRawOffset() / HOUR_MILLIS + + "\t" + gmtOffsetMillis / HOUR_MILLIS + "\n" + - "\t" + getTimeZone().getDSTSavings() / HOUR_MILLIS + + "\t" + dstOffsetMillis / HOUR_MILLIS + "\n" + ""; } @@ -620,6 +628,10 @@ public int hashCode() { * @see java.lang.Object#toString() */ public String toString() { + Calendar cal = Calendar.getInstance(getTimeZone()); + long gmtOffsetMillis = TimeZoneUtils.getTimezoneOffsetAt(cal); + long dstOffsetMillis = getTimeZone().getDSTSavings(); + return "\nLocation Name:\t\t\t" + getLocationName() + "\nLatitude:\t\t\t" + getLatitude() + "\u00B0" + "\nLongitude:\t\t\t" + getLongitude() + "\u00B0" + @@ -627,8 +639,8 @@ public String toString() { "\nTimezone ID:\t\t\t" + getTimeZone().getID() + "\nTimezone Display Name:\t\t" + getTimeZone().getDisplayName() + " (" + getTimeZone().getDisplayName(false, TimeZone.SHORT) + ")" + - "\nTimezone GMT Offset:\t\t" + getTimeZone().getRawOffset() / HOUR_MILLIS + - "\nTimezone DST Offset:\t\t" + getTimeZone().getDSTSavings() / HOUR_MILLIS; + "\nTimezone GMT Offset:\t\t" + gmtOffsetMillis / HOUR_MILLIS + + "\nTimezone DST Offset:\t\t" + dstOffsetMillis / HOUR_MILLIS; } /** diff --git a/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java b/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java index ec9a0453..f60d29cd 100644 --- a/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java +++ b/src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java @@ -15,7 +15,10 @@ */ package com.kosherjava.zmanim.util; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Calendar; +import java.util.TimeZone; /** * Implementation of sunrise and sunset methods to calculate astronomical times based on the Universal Coordinated Time (UTC) diff --git a/src/main/java/com/kosherjava/zmanim/util/TimeZoneUtils.java b/src/main/java/com/kosherjava/zmanim/util/TimeZoneUtils.java new file mode 100644 index 00000000..82fa787a --- /dev/null +++ b/src/main/java/com/kosherjava/zmanim/util/TimeZoneUtils.java @@ -0,0 +1,69 @@ +/* + * Zmanim Java API + * Copyright (C) 2004-2025 Eliyahu Hershfeld + * + * 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: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + */ +package com.kosherjava.zmanim.util; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * Utility class for timezone-related calculations using java.time APIs. + * + * @author © Eliyahu Hershfeld 2004 - 2025 + */ +public class TimeZoneUtils { + + /** + * Gets the timezone offset in milliseconds at a specific instant using java.time APIs. + * This method correctly handles Daylight Saving Time (DST) changes by calculating the offset + * at the specific date/time represented by the Calendar. + * + * @param calendar the Calendar containing the date/time and timezone to calculate the offset for + * @return the timezone offset in milliseconds + */ + public static long getTimezoneOffsetAt(Calendar calendar) { + TimeZone timeZone = calendar.getTimeZone(); + long unixTimestampMillis = calendar.getTimeInMillis(); + ZoneId zoneId = timeZone.toZoneId(); + Instant instant = Instant.ofEpochMilli(unixTimestampMillis); + ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId); + return zonedDateTime.getOffset().getTotalSeconds() * 1000; + } + + /** + * Adds one day to the given Calendar by converting it to a ZonedDateTime, + * adding a day, and converting it back to a Calendar. + * The returned Calendar will have the same TimeZone as the input Calendar. + * + * @param calendar the Calendar to add a day to + * @return a new Calendar with one day added, preserving the original TimeZone + */ + public static Calendar addDay(Calendar calendar, int days) { + TimeZone timeZone = calendar.getTimeZone(); + long unixTimestampMillis = calendar.getTimeInMillis(); + ZoneId zoneId = timeZone.toZoneId(); + Instant instant = Instant.ofEpochMilli(unixTimestampMillis); + ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, zoneId); + ZonedDateTime nextDay = zonedDateTime.plusDays(days); + Instant nextDayInstant = nextDay.toInstant(); + Calendar result = Calendar.getInstance(timeZone); + result.setTimeInMillis(nextDayInstant.toEpochMilli()); + return result; + } +} +