From 277e3214809df01783d72f4458a4f6889bd7a4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Mon, 28 Oct 2024 10:41:21 +0100 Subject: [PATCH] AstroCalculator DST change FIX --- pom.xml | 6 ++ .../java/de/ph87/homeautomation/Config.java | 2 - .../schedule/ScheduleCalculator.java | 16 +++-- .../schedule/astro/AstroCalculator.java | 22 ++---- ...latorDaylightSavingTimeTransitionTest.java | 70 +++++++++++++++++++ 5 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 src/test/java/de/ph87/homeautomation/schedule/ScheduleCalculatorDaylightSavingTimeTransitionTest.java diff --git a/pom.xml b/pom.xml index 03a117f..5c7e521 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,12 @@ 1.2 + + org.springframework.boot + spring-boot-starter-test + test + + diff --git a/src/main/java/de/ph87/homeautomation/Config.java b/src/main/java/de/ph87/homeautomation/Config.java index 0cd7502..959b847 100644 --- a/src/main/java/de/ph87/homeautomation/Config.java +++ b/src/main/java/de/ph87/homeautomation/Config.java @@ -13,8 +13,6 @@ public class Config { private double longitude = 6.9645334; - private String timezone = "Europe/Berlin"; - private boolean insertDemoData = false; } diff --git a/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java b/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java index 3a429f1..5d30a36 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java +++ b/src/main/java/de/ph87/homeautomation/schedule/ScheduleCalculator.java @@ -11,6 +11,8 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Comparator; import java.util.Optional; @@ -55,12 +57,12 @@ public class ScheduleCalculator { entry.setNextClearTimestamp(null); return; } - ZonedDateTime midnight = now.withHour(0).withMinute(0).withSecond(0).withNano(0); - ZonedDateTime next = calculateEntryForDay(entry, midnight); + LocalDate day = now.toLocalDate(); + ZonedDateTime next = calculateEntryForDay(entry, day); while (next != null && (!next.isAfter(now) || !isAfterLast(entry, next) || !isWeekdayEnabled(entry, next))) { log.debug(" -- skipping: next={}", next); - midnight = midnight.plusDays(1); - next = calculateEntryForDay(entry, midnight); + day = day.plusDays(1); + next = calculateEntryForDay(entry, day); } log.debug(" => {}", next); entry.setNextClearTimestamp(next); @@ -70,14 +72,14 @@ public class ScheduleCalculator { return entry.getLastClearTimestamp() == null || next.isAfter(entry.getLastClearTimestamp()); } - private ZonedDateTime calculateEntryForDay(final ScheduleEntry entry, final ZonedDateTime midnight) { + private ZonedDateTime calculateEntryForDay(final ScheduleEntry entry, final LocalDate day) { switch (entry.getType()) { case TIME: - return midnight.withHour(entry.getHour()).withMinute(entry.getMinute()).withSecond(entry.getSecond()); + return day.atStartOfDay().atZone(ZoneId.systemDefault()).withHour(entry.getHour()).withMinute(entry.getMinute()).withSecond(entry.getSecond()); case SUNRISE: case SUNSET: final boolean sunrise = entry.getType() == ScheduleEntryType.SUNRISE; - return astroCalculator.forDay(midnight, sunrise, entry.getZenith()); + return astroCalculator.forDay(day, sunrise, entry.getZenith()).withLaterOffsetAtOverlap(); default: log.error("AstroEvent not implemented: {}", entry.getType()); break; diff --git a/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java index 5bef5cb..b88714a 100644 --- a/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java +++ b/src/main/java/de/ph87/homeautomation/schedule/astro/AstroCalculator.java @@ -8,10 +8,12 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.TimeZone; @Service @RequiredArgsConstructor @@ -19,25 +21,15 @@ public class AstroCalculator { private final Config config; - public ZonedDateTime next(final ZonedDateTime now, final boolean sunrise, final double zenith) { - ZonedDateTime day = now.truncatedTo(ChronoUnit.DAYS); - ZonedDateTime next; - do { - next = forDay(day, sunrise, zenith); - day = day.plusDays(1); - } while (next != null && !next.isAfter(now)); - return next; - } - - public ZonedDateTime forDay(final ZonedDateTime midnight, final boolean sunrise, final double zenith) { + public ZonedDateTime forDay(final LocalDate day, final boolean sunrise, final double zenith) { final Location location = new Location(config.getLatitude(), config.getLongitude()); - final SolarEventCalculator calculator = new SolarEventCalculator(location, config.getTimezone()); - final Calendar calendar = GregorianCalendar.from(midnight); + final SolarEventCalculator calculator = new SolarEventCalculator(location, TimeZone.getTimeZone("UTC")); + final Calendar calendar = GregorianCalendar.from(day.atStartOfDay(ZoneId.of("UTC"))); final Calendar nextCalendar = forDay(calculator, sunrise, new Zenith(zenith), calendar); if (nextCalendar == null) { return null; } - return ZonedDateTime.ofInstant(Instant.ofEpochMilli(nextCalendar.getTimeInMillis()), midnight.getZone()); + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(nextCalendar.getTimeInMillis()), ZoneId.systemDefault()); } private Calendar forDay(final SolarEventCalculator calculator, final boolean sunrise, final Zenith solarZenith, final Calendar calendar) { diff --git a/src/test/java/de/ph87/homeautomation/schedule/ScheduleCalculatorDaylightSavingTimeTransitionTest.java b/src/test/java/de/ph87/homeautomation/schedule/ScheduleCalculatorDaylightSavingTimeTransitionTest.java new file mode 100644 index 0000000..bbfbbe1 --- /dev/null +++ b/src/test/java/de/ph87/homeautomation/schedule/ScheduleCalculatorDaylightSavingTimeTransitionTest.java @@ -0,0 +1,70 @@ +package de.ph87.homeautomation.schedule; + +import de.ph87.homeautomation.Config; +import de.ph87.homeautomation.schedule.astro.AstroCalculator; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Slf4j +class ScheduleCalculatorDaylightSavingTimeTransitionTest { + + private static final ZonedDateTime DAY_OF_DST_SET_BACK = ZonedDateTime.of(2024, 10, 27, 0, 0, 0, 0, ZoneId.of("Europe/Berlin")); + + private static final AstroCalculator astroCalculator = new AstroCalculator(new Config()); + + @Test + void withHour() { + final ZonedDateTime morning = DAY_OF_DST_SET_BACK.withHour(6); + assertEquals(6, morning.getHour()); + assertEquals(0, morning.getMinute()); + assertEquals(0, morning.getSecond()); + assertEquals(0, morning.getNano()); + assertEquals(3600, morning.getOffset().getTotalSeconds()); + } + + @Test + void sunrise_dstSetBack() { + final ZonedDateTime morning = astroCalculator.forDay(DAY_OF_DST_SET_BACK.toLocalDate(), true, 90); + assertEquals(7, morning.getHour()); + assertEquals(19, morning.getMinute()); + assertEquals(0, morning.getSecond()); + assertEquals(0, morning.getNano()); + assertEquals(3600, morning.getOffset().getTotalSeconds()); + } + + @Test + void sunset_dstSetBack() { + final ZonedDateTime morning = astroCalculator.forDay(DAY_OF_DST_SET_BACK.toLocalDate(), false, 90); + assertEquals(17, morning.getHour()); + assertEquals(13, morning.getMinute()); + assertEquals(0, morning.getSecond()); + assertEquals(0, morning.getNano()); + assertEquals(3600, morning.getOffset().getTotalSeconds()); + } + + @Test + void sunrise_noDstChange() { + final ZonedDateTime morning = astroCalculator.forDay(DAY_OF_DST_SET_BACK.minusDays(1).toLocalDate(), true, 90); + assertEquals(8, morning.getHour()); + assertEquals(17, morning.getMinute()); + assertEquals(0, morning.getSecond()); + assertEquals(0, morning.getNano()); + assertEquals(7200, morning.getOffset().getTotalSeconds()); + } + + @Test + void sunset_noDstChange() { + final ZonedDateTime morning = astroCalculator.forDay(DAY_OF_DST_SET_BACK.minusDays(1).toLocalDate(), false, 90); + assertEquals(18, morning.getHour()); + assertEquals(14, morning.getMinute()); + assertEquals(0, morning.getSecond()); + assertEquals(0, morning.getNano()); + assertEquals(7200, morning.getOffset().getTotalSeconds()); + } + +} \ No newline at end of file