From bf30a18ceff24da9791319e0a37bd9906a9c4fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20K=C3=B6pp?= Date: Thu, 23 Apr 2026 18:52:11 +0200 Subject: [PATCH 1/6] [DRAFT/UNTESTED] Fix(SunRise): Wrong Time when on Previous UTC-Day --- src/OpenKNX/Sun/SunCalculation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/OpenKNX/Sun/SunCalculation.cpp b/src/OpenKNX/Sun/SunCalculation.cpp index f3120277..2b06eb44 100644 --- a/src/OpenKNX/Sun/SunCalculation.cpp +++ b/src/OpenKNX/Sun/SunCalculation.cpp @@ -51,15 +51,16 @@ namespace OpenKNX double rise, set; // sunrise/sunset calculation + // TODO check the return {<,=,>}0 for special cases SunRiseAndSet::sunRiseSet(utc.year, utc.month, utc.day, longitude, latitude, -35.0 / 60.0, 1, &rise, &set); - _sunRiseUtc.hour = (int)floor(rise); + _sunRiseUtc.hour = (((int)floor(rise) % 24) + 24) % 24, // ensure positive hour in [0;24[ _sunRiseUtc.minute = (int)(60 * (rise - floor(rise))); _sunRiseUtc.second = 0; _sunRiseLocalTime = DateTime(utc.year, utc.month, utc.day, _sunRiseUtc.hour, _sunRiseUtc.minute, _sunRiseUtc.second, DateTimeTypeUTC).toLocalTime(); - _sunSetUtc.hour = (int)floor(set); + _sunSetUtc.hour = (((int)floor(set) % 24) + 24) % 24, // ensure positive hour in [0;24[ // Note: negative values expected for rise only _sunSetUtc.minute = (int)(60 * (set - floor(set))); _sunSetUtc.second = 0; _sunSetLocalTime = DateTime(utc.year, utc.month, utc.day, _sunSetUtc.hour, _sunSetUtc.minute, _sunSetUtc.second, DateTimeTypeUTC).toLocalTime(); From 5c0ee40086e2c99038c9be2ceeeaf9fc451f025d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20K=C3=B6pp?= Date: Thu, 23 Apr 2026 19:13:28 +0200 Subject: [PATCH 2/6] [DRAFT/UNTESTED] Fix(SunRise): Compensate Previous UTC-Day --- src/OpenKNX/Sun/SunCalculation.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/OpenKNX/Sun/SunCalculation.cpp b/src/OpenKNX/Sun/SunCalculation.cpp index 2b06eb44..0b5c8a2b 100644 --- a/src/OpenKNX/Sun/SunCalculation.cpp +++ b/src/OpenKNX/Sun/SunCalculation.cpp @@ -58,12 +58,18 @@ namespace OpenKNX _sunRiseUtc.hour = (((int)floor(rise) % 24) + 24) % 24, // ensure positive hour in [0;24[ _sunRiseUtc.minute = (int)(60 * (rise - floor(rise))); _sunRiseUtc.second = 0; - _sunRiseLocalTime = DateTime(utc.year, utc.month, utc.day, _sunRiseUtc.hour, _sunRiseUtc.minute, _sunRiseUtc.second, DateTimeTypeUTC).toLocalTime(); + OpennKNX::DateTime dtRise = DateTime(utc.year, utc.month, utc.day, _sunRiseUtc.hour, _sunRiseUtc.minute, _sunRiseUtc.second, DateTimeTypeUTC); + if (rise < 0) // compensate different utc-day, to prevent issues with local time conversion around DST-change + dtRise.addDays(-1); // expected exactly one day in past only + _sunRiseLocalTime = dtRise.toLocalTime(); _sunSetUtc.hour = (((int)floor(set) % 24) + 24) % 24, // ensure positive hour in [0;24[ // Note: negative values expected for rise only _sunSetUtc.minute = (int)(60 * (set - floor(set))); _sunSetUtc.second = 0; - _sunSetLocalTime = DateTime(utc.year, utc.month, utc.day, _sunSetUtc.hour, _sunSetUtc.minute, _sunSetUtc.second, DateTimeTypeUTC).toLocalTime(); + OpennKNX::DateTime dtSet = DateTime(utc.year, utc.month, utc.day, _sunSetUtc.hour, _sunSetUtc.minute, _sunSetUtc.second, DateTimeTypeUTC); + if (set < 0) // compensate different utc-day, to prevent issues with local time conversion around DST-change + dtSet.addDays(-1); // expected exactly one day in past only + _sunSetLocalTime = dtSet.toLocalTime(); _sunCalculationValid = true; } From 45014b70b35280986fcf03ff98dce1d884dd5e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20K=C3=B6pp?= Date: Thu, 23 Apr 2026 19:20:48 +0200 Subject: [PATCH 3/6] fixup! [DRAFT/UNTESTED] Fix(SunRise): Compensate Previous UTC-Day --- src/OpenKNX/Sun/SunCalculation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenKNX/Sun/SunCalculation.cpp b/src/OpenKNX/Sun/SunCalculation.cpp index 0b5c8a2b..e96e9dff 100644 --- a/src/OpenKNX/Sun/SunCalculation.cpp +++ b/src/OpenKNX/Sun/SunCalculation.cpp @@ -58,7 +58,7 @@ namespace OpenKNX _sunRiseUtc.hour = (((int)floor(rise) % 24) + 24) % 24, // ensure positive hour in [0;24[ _sunRiseUtc.minute = (int)(60 * (rise - floor(rise))); _sunRiseUtc.second = 0; - OpennKNX::DateTime dtRise = DateTime(utc.year, utc.month, utc.day, _sunRiseUtc.hour, _sunRiseUtc.minute, _sunRiseUtc.second, DateTimeTypeUTC); + DateTime dtRise = DateTime(utc.year, utc.month, utc.day, _sunRiseUtc.hour, _sunRiseUtc.minute, _sunRiseUtc.second, DateTimeTypeUTC); if (rise < 0) // compensate different utc-day, to prevent issues with local time conversion around DST-change dtRise.addDays(-1); // expected exactly one day in past only _sunRiseLocalTime = dtRise.toLocalTime(); @@ -66,7 +66,7 @@ namespace OpenKNX _sunSetUtc.hour = (((int)floor(set) % 24) + 24) % 24, // ensure positive hour in [0;24[ // Note: negative values expected for rise only _sunSetUtc.minute = (int)(60 * (set - floor(set))); _sunSetUtc.second = 0; - OpennKNX::DateTime dtSet = DateTime(utc.year, utc.month, utc.day, _sunSetUtc.hour, _sunSetUtc.minute, _sunSetUtc.second, DateTimeTypeUTC); + DateTime dtSet = DateTime(utc.year, utc.month, utc.day, _sunSetUtc.hour, _sunSetUtc.minute, _sunSetUtc.second, DateTimeTypeUTC); if (set < 0) // compensate different utc-day, to prevent issues with local time conversion around DST-change dtSet.addDays(-1); // expected exactly one day in past only _sunSetLocalTime = dtSet.toLocalTime(); From b0edfe9896d4ffa43289d510b384021cb42177d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20K=C3=B6pp?= Date: Fri, 24 Apr 2026 22:34:51 +0200 Subject: [PATCH 4/6] [DRAFT/UNTESTED] Fix(SunRise)/Rework: Support `DateTime` with Negative Hours --- src/OpenKNX/DateTime.cpp | 15 +++++++++++++-- src/OpenKNX/DateTime.h | 2 +- src/OpenKNX/Sun/SunCalculation.cpp | 26 ++++++++++++++------------ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/OpenKNX/DateTime.cpp b/src/OpenKNX/DateTime.cpp index 82bfb1a5..00c1125a 100644 --- a/src/OpenKNX/DateTime.cpp +++ b/src/OpenKNX/DateTime.cpp @@ -26,17 +26,28 @@ namespace OpenKNX } } - DateTime::DateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, DateTimeType type) + DateTime::DateTime(uint16_t year, uint8_t month, uint8_t day, int32_t hour, uint8_t minute, uint8_t second, DateTimeType type) { + // TODO: Check need and support for handling out of range field values. + // Note: Hotfix for negative hours to solve broken SunRise + + // 1. setting fiels ONLY this->year = year; this->month = month; this->day = day; - this->hour = hour; + this->hour = hour; // note: additional special handling at the end, for handle hours out of range this->minute = minute; this->second = second; this->isDst = type == DateTimeTypeLocalTimeDST; this->isUtc = type == DateTimeTypeUTC; this->isLocalTime = type != DateTimeTypeUTC; + + // 2. after setting all values, handle hours out of range (e.g. negative!) + if (hour < 0 || 24 <= hour) + { + this->hour = 0; + addHours(hour); + } } DateTime::DateTime(time_t time, bool createUtc) diff --git a/src/OpenKNX/DateTime.h b/src/OpenKNX/DateTime.h index abe903f4..80e6cc9d 100644 --- a/src/OpenKNX/DateTime.h +++ b/src/OpenKNX/DateTime.h @@ -37,7 +37,7 @@ namespace OpenKNX struct DateTime : public DateOnly, public TimeOnly { DateTime() = default; - DateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, DateTimeType type); + DateTime(uint16_t year, uint8_t month, uint8_t day, int32_t hour, uint8_t minute, uint8_t second, DateTimeType type); DateTime(time_t time, bool createUtc = false); diff --git a/src/OpenKNX/Sun/SunCalculation.cpp b/src/OpenKNX/Sun/SunCalculation.cpp index e96e9dff..0d7e69c9 100644 --- a/src/OpenKNX/Sun/SunCalculation.cpp +++ b/src/OpenKNX/Sun/SunCalculation.cpp @@ -55,20 +55,22 @@ namespace OpenKNX SunRiseAndSet::sunRiseSet(utc.year, utc.month, utc.day, longitude, latitude, -35.0 / 60.0, 1, &rise, &set); - _sunRiseUtc.hour = (((int)floor(rise) % 24) + 24) % 24, // ensure positive hour in [0;24[ - _sunRiseUtc.minute = (int)(60 * (rise - floor(rise))); - _sunRiseUtc.second = 0; - DateTime dtRise = DateTime(utc.year, utc.month, utc.day, _sunRiseUtc.hour, _sunRiseUtc.minute, _sunRiseUtc.second, DateTimeTypeUTC); - if (rise < 0) // compensate different utc-day, to prevent issues with local time conversion around DST-change - dtRise.addDays(-1); // expected exactly one day in past only + const int32_t sunRiseUtcHour = (int32_t)floor(rise) + const uint8_t sunRiseUtcMinute = (int32_t)(60 * (rise - floor(rise))); + const uint8_t sunRiseUtcSecond = 0; + DateTime dtRise = DateTime(utc.year, utc.month, utc.day, sunRiseUtcHour, sunRiseUtcMinute, sunRiseUtcSecond, DateTimeTypeUTC); + _sunRiseUtc.hour = dtRise.hour; + _sunRiseUtc.minute = dtRise.minute; + _sunRiseUtc.second = dtRise.second; _sunRiseLocalTime = dtRise.toLocalTime(); - _sunSetUtc.hour = (((int)floor(set) % 24) + 24) % 24, // ensure positive hour in [0;24[ // Note: negative values expected for rise only - _sunSetUtc.minute = (int)(60 * (set - floor(set))); - _sunSetUtc.second = 0; - DateTime dtSet = DateTime(utc.year, utc.month, utc.day, _sunSetUtc.hour, _sunSetUtc.minute, _sunSetUtc.second, DateTimeTypeUTC); - if (set < 0) // compensate different utc-day, to prevent issues with local time conversion around DST-change - dtSet.addDays(-1); // expected exactly one day in past only + const int32_t sunSetUtcHour = (int32_t)floor(set); + const uint8_t sunSetUtcMinute = (int32_t)(60 * (set - floor(set))); + const uint8_t sunSetUtcSecond = 0; + DateTime dtSet = DateTime(utc.year, utc.month, utc.day, sunSetUtcHour, sunSetUtcMinute, sunSetUtcSecond, DateTimeTypeUTC); + _sunSetUtc.hour = dtSet.hour; + _sunSetUtc.minute = dtSet.minute; + _sunSetUtc.second = dtSet.second; _sunSetLocalTime = dtSet.toLocalTime(); _sunCalculationValid = true; From 9283aa9061cad17753b70d7e58cf2f3d244413c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20K=C3=B6pp?= Date: Sun, 26 Apr 2026 21:54:18 +0200 Subject: [PATCH 5/6] Include Fix for SunRise-Calc and DateTime in Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fa42aba..ee72af02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * Fix: DST switch for NTP client * Fix/Change: Save data to flash in case of restart from console +* Fix: SunRise-Calculation was wrong for UTC-day-change (this was not an issue for Europe, but e.g. New Zealand) + * Fix/Change: Support negative hours in TimeDate constructor * Update: Increase minimum OpenKNXproducer version to 4.3.5 * Fix: Suppress warnings for intentionally overlapping parameters * Feature: Indicate unconfigured and PA not set via PROG-LED From 088b8bbbdf03d4cec010c6a061bb0b9304c4852d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20K=C3=B6pp?= <121643070+cornelius-koepp@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:00:11 +0200 Subject: [PATCH 6/6] fixup! [DRAFT/UNTESTED] Fix(SunRise)/Rework: Support DateTime with Negative Hours Syntax Error --- src/OpenKNX/Sun/SunCalculation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenKNX/Sun/SunCalculation.cpp b/src/OpenKNX/Sun/SunCalculation.cpp index 0d7e69c9..e88d3dec 100644 --- a/src/OpenKNX/Sun/SunCalculation.cpp +++ b/src/OpenKNX/Sun/SunCalculation.cpp @@ -55,7 +55,7 @@ namespace OpenKNX SunRiseAndSet::sunRiseSet(utc.year, utc.month, utc.day, longitude, latitude, -35.0 / 60.0, 1, &rise, &set); - const int32_t sunRiseUtcHour = (int32_t)floor(rise) + const int32_t sunRiseUtcHour = (int32_t)floor(rise); const uint8_t sunRiseUtcMinute = (int32_t)(60 * (rise - floor(rise))); const uint8_t sunRiseUtcSecond = 0; DateTime dtRise = DateTime(utc.year, utc.month, utc.day, sunRiseUtcHour, sunRiseUtcMinute, sunRiseUtcSecond, DateTimeTypeUTC); @@ -125,4 +125,4 @@ namespace OpenKNX } // namespace Sun } // namespace OpenKNX -#endif \ No newline at end of file +#endif