diff --git a/openBeken_win32_mvsc2017.vcxproj b/openBeken_win32_mvsc2017.vcxproj
index ff57c94a7..393039a89 100644
--- a/openBeken_win32_mvsc2017.vcxproj
+++ b/openBeken_win32_mvsc2017.vcxproj
@@ -745,6 +745,7 @@
+
@@ -1173,4 +1174,4 @@
-
\ No newline at end of file
+
diff --git a/openBeken_win32_mvsc2017.vcxproj.filters b/openBeken_win32_mvsc2017.vcxproj.filters
index 3420535df..3ecf3e506 100644
--- a/openBeken_win32_mvsc2017.vcxproj.filters
+++ b/openBeken_win32_mvsc2017.vcxproj.filters
@@ -274,6 +274,7 @@
+
@@ -535,4 +536,4 @@
-
\ No newline at end of file
+
diff --git a/src/cmnds/cmd_if.c b/src/cmnds/cmd_if.c
index d1697297c..69654c341 100644
--- a/src/cmnds/cmd_if.c
+++ b/src/cmnds/cmd_if.c
@@ -298,7 +298,11 @@ float getMonth(const char *s) {
float getMDay(const char *s) {
return NTP_GetMDay();
}
-
+#if ENABLE_NTP_DST
+float isDST(const char *s){
+ return Time_IsDST();
+}
+#endif
#if ENABLE_NTP_SUNRISE_SUNSET
float getSunrise(const char *s) {
@@ -475,6 +479,13 @@ const constant_t g_constants[] = {
////cnstdetail:"descr":"",
////cnstdetail:"requires":""}
{ "$today", &getToday },
+#if ENABLE_NTP_DST
+ ////cnstdetail:{"name":"$isDST",
+ ////cnstdetail:"title":"$isDST",
+ ////cnstdetail:"descr":"",
+ ////cnstdetail:"requires":""}
+ { "$isDST", &isDST },
+#endif
#if ENABLE_NTP_SUNRISE_SUNSET
////cnstdetail:{"name":"$sunrise",
////cnstdetail:"title":"$sunrise",
diff --git a/src/driver/drv_ntp.c b/src/driver/drv_ntp.c
index b9ff90fdf..4cbc8678e 100644
--- a/src/driver/drv_ntp.c
+++ b/src/driver/drv_ntp.c
@@ -14,6 +14,9 @@
#include "drv_ntp.h"
+#define LTSTR "Local Time: %04d-%02d-%02d %02d:%02d:%02d"
+#define LTM2TIME(T) (T)->tm_year+1900, (T)->tm_mon+1, (T)->tm_mday, (T)->tm_hour, (T)->tm_min, (T)->tm_sec
+
extern void NTP_Init_Events(void);
extern void NTP_RunEvents(unsigned int newTime, bool bTimeValid);
@@ -22,6 +25,19 @@ extern void NTP_CalculateSunrise(byte *outHour, byte *outMinute);
extern void NTP_CalculateSunset(byte *outHour, byte *outMinute);
#endif
+#if ENABLE_NTP_DST
+static uint32_t Start_DST_epoch=0 , End_DST_epoch=0, next_DST_switch_epoch=0;
+static uint8_t nthWeekEnd, monthEnd, dayEnd, hourEnd, nthWeekStart, monthStart, dayStart, hourStart;
+static int8_t g_DST_offset=0, g_DST=-128; // g_DST_offset: offset during DST; 0: unset / g_DST: actual DST_offset in hours; -128: not initialised
+#define useDST (g_DST_offset)
+const uint32_t SECS_PER_MIN = 60UL;
+const uint32_t SECS_PER_HOUR = 3600UL;
+const uint32_t SECS_PER_DAY = 3600UL * 24UL;
+const uint32_t MINS_PER_HOUR = 60UL;
+#define LEAP_YEAR(Y) ((!(Y%4) && (Y%100)) || !(Y%400))
+static const uint8_t DaysMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+#endif
+
#define LOG_FEATURE LOG_FEATURE_NTP
typedef struct
@@ -104,7 +120,18 @@ commandResult_t NTP_SetTimeZoneOfs(const void *context, const char *cmd, const c
}
g_ntpTime -= oldOfs;
g_ntpTime += g_timeOffsetSeconds;
- addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP offset set");
+#if ENABLE_NTP_DST
+// in rare cases time can be decreased so time of next DST is wrong
+// e.g. by mistake we set offset to two hours in EU and we have just passed start of summertime - next DST switch will be end of summertime
+// if we now adjust the clock to the correct offset of one hour, we are slightliy before start of summertime
+// so just to be sure, recalculate DST in any case
+ setDST(1); // setDST will take care of all details
+#endif
+ addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"NTP offset set"
+#if ENABLE_NTP_DST
+ " - DST offset set to %i hours",g_DST
+#endif
+ );
return CMD_RES_OK;
}
@@ -185,6 +212,182 @@ commandResult_t NTP_Info(const void *context, const char *cmd, const char *args,
addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "Server=%s, Time offset=%d", CFG_GetNTPServer(), g_timeOffsetSeconds);
return CMD_RES_OK;
}
+
+#if ENABLE_NTP_DST
+
+// derived from tasmotas "RuleToTime"
+
+uint32_t RuleToTime(uint8_t dow, uint8_t mo, uint8_t week, uint8_t hr, int yr) {
+ struct tm tm={0};
+ uint32_t t=0;
+ int i;
+ uint8_t m=mo-1; // we use struct tm here, so month values are 0..11
+ uint8_t w=week; // m and w are copies of mo(nth) and week
+
+ if (0 == w) { // for "Last XX" (w=0), we compute first occurence in following month and go back 7 days
+ if (++m > 11) { // so go to the next month ...
+ m = 0; // .. and to next year, if we need last XX in December
+ yr++;
+ }
+ w = 1; // search first week of next month, we will subtract 7 days later
+ }
+
+ // avoid mktime - this enlarges the image especially for BL602!
+ // so calculate seconds from epoch locally
+ // we start by calculating the number of days since 1970 - will also be used to get weekday
+ uint16_t days;
+ days = (yr - 1970) * 365; // days per full years
+ // add one day every leap year - first leap after 1970 is 1972, possible leap years every 4 years
+ for (i=1972; i < yr; i+=4) days += LEAP_YEAR(i);
+ for (i=0; i begin before end
+ if (g_ntpTime < Start_DST_epoch) {
+ // we are in winter time before start of summer time
+ next_DST_switch_epoch=Start_DST_epoch;
+ g_DST=0;
+// tempt = (time_t)Start_DST_epoch;
+// ADDLOG_INFO(LOG_FEATURE_RAW, "Before first DST switch in %i. Info: DST starts at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tempt));
+ } else if (g_ntpTime < End_DST_epoch ){
+ // we in summer time
+ next_DST_switch_epoch=End_DST_epoch;
+ g_DST=g_DST_offset;
+// tempt = (time_t)End_DST_epoch;
+// ADDLOG_INFO(LOG_FEATURE_RAW, "In DST of %i. Info: DST ends at %lu (%.24s local time)\r\n",year,End_DST_epoch,ctime(&tempt));
+ } else {
+ // we are in winter time, after summer time --> next DST starts in next year
+ Start_DST_epoch = RuleToTime(dayStart,monthStart,nthWeekStart,hourStart,year+1);
+ next_DST_switch_epoch=Start_DST_epoch;
+ g_DST=0;
+// tempt = (time_t)Start_DST_epoch;
+// ADDLOG_INFO(LOG_FEATURE_RAW, "After DST in %i. Info: Next DST start in next year at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tempt));
+ }
+ } else { // so end of DST before begin of DST --> southern
+ if (g_ntpTime < End_DST_epoch) {
+ // we in summer time at beginning of the yeay
+ next_DST_switch_epoch=End_DST_epoch;
+ g_DST=g_DST_offset;
+// tempt = (time_t)End_DST_epoch;
+// ADDLOG_INFO(LOG_FEATURE_RAW, "In first DST period of %i. Info: DST ends at %lu (%.24s local time)\r\n",year,End_DST_epoch,ctime(&tempt));
+ } else if (g_ntpTime < Start_DST_epoch ){
+ // we are in winter time
+ next_DST_switch_epoch=Start_DST_epoch;
+ g_DST=0;
+// tempt = (time_t)Start_DST_epoch;
+// ADDLOG_INFO(LOG_FEATURE_RAW, "Regular time of %i. Info: DST starts at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tempt));
+ } else {
+ // we in summer time at the end of the year --> DST will end next year
+ End_DST_epoch = RuleToTime(dayEnd,monthEnd,nthWeekEnd,hourEnd,year+1);
+ next_DST_switch_epoch=End_DST_epoch;
+ g_DST=g_DST_offset;
+// tempt = (time_t)End_DST_epoch;
+// ADDLOG_INFO(LOG_FEATURE_RAW, "In second DST of %i. Info: DST ends next year at %lu (%.24s local time)\r\n",year,End_DST_epoch,ctime(&tempt));
+ }
+ }
+ g_ntpTime += (g_DST-old_DST)*3600*setNTP;
+ tempt = (time_t)next_DST_switch_epoch;
+
+ struct tm *ltm;
+ ltm = gmtime(&tempt);
+ ADDLOG_INFO(LOG_FEATURE_RAW, "In %s time - next DST switch at %lu (" LTSTR ")\r\n",
+ (g_DST)?"summer":"standard", next_DST_switch_epoch, LTM2TIME(ltm));
+ return g_DST;
+ }
+ else return 0; // DST not (yet) set or can't be calculated (if ntp not synced)
+
+}
+
+int IsDST()
+{
+ if (( g_DST == -128) || (g_ntpTime > next_DST_switch_epoch)) return (setDST(1)); // only in case we don't know DST status, calculate it - and while at it: set ntpTime correctly...
+ return (g_DST); // otherwise we can safely return the prevously calkulated value
+}
+
+commandResult_t CLOCK_CalcDST(const void *context, const char *cmd, const char *args, int cmdFlags) {
+ int year=NTP_GetYear();
+ time_t te,tb; // time_t of timestamps needed for ctime() to get string of timestamp
+
+ Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES);
+ // following check must be done after 'Tokenizer_TokenizeString',
+ // so we know arguments count in Tokenizer. 'cmd' argument is
+ // only for warning display
+ if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 8)) {
+ return CMD_RES_NOT_ENOUGH_ARGUMENTS;
+ }
+
+// ADDLOG_INFO(LOG_FEATURE_RAW, "CLOCK_SetConfigs: we have %u Args\r\n", Tokenizer_GetArgsCount());
+ nthWeekEnd = Tokenizer_GetArgInteger(0);
+ monthEnd = Tokenizer_GetArgInteger(1);
+ dayEnd = Tokenizer_GetArgInteger(2);
+ hourEnd = Tokenizer_GetArgInteger(3);
+ nthWeekStart = Tokenizer_GetArgInteger(4);
+ monthStart = Tokenizer_GetArgInteger(5);
+ dayStart = Tokenizer_GetArgInteger(6);
+ hourStart = Tokenizer_GetArgInteger(7);
+ g_DST_offset=Tokenizer_GetArgIntegerDefault(8, 1);
+ ADDLOG_INFO(LOG_FEATURE_RAW, "read values: %u,%u,%u,%u,%u,%u,%u,%u,(%u)\r\n", nthWeekEnd, monthEnd, dayEnd, hourEnd, nthWeekStart, monthStart, dayStart, hourStart,g_DST_offset);
+
+/* Start_DST_epoch = RuleToTime(dayStart,monthStart,nthWeekStart,hourStart,year);
+ End_DST_epoch = RuleToTime(dayEnd,monthEnd,nthWeekEnd,hourEnd,year);
+ te=(time_t)End_DST_epoch;
+ tb=(time_t)Start_DST_epoch;
+
+ ADDLOG_INFO(LOG_FEATURE_RAW, "Calculated DST switch epochs in %i. DST start at %lu (%.24s local time) - DST end at %lu (%.24s local time)\r\n",year,Start_DST_epoch,ctime(&tb),End_DST_epoch,ctime(&te));
+*/
+ return CMD_RES_OK;
+};
+
+
+
+#endif
+
+
+
int NTP_GetWeekDay() {
struct tm *ltm;
@@ -269,11 +472,19 @@ int NTP_GetYear() {
return ltm->tm_year+1900;
}
+#if ENABLE_NTP_DST
+int Time_IsDST(){
+ return IsDST();
+}
+#endif
#if WINDOWS
bool b_ntp_simulatedTime = false;
void NTP_SetSimulatedTime(unsigned int timeNow) {
g_ntpTime = timeNow;
g_ntpTime += g_timeOffsetSeconds;
+#if ENABLE_NTP_DST
+ g_ntpTime += setDST(0)*3600;
+#endif
g_synced = true;
b_ntp_simulatedTime = true;
}
@@ -309,7 +520,13 @@ void NTP_Init() {
#if ENABLE_CALENDAR_EVENTS
NTP_Init_Events();
#endif
-
+#if ENABLE_NTP_DST
+ //cmddetail:{"name":"CLOCK_CalcDST","args":"[nthWeekEnd monthEnd dayEnd hourEnd nthWeekStart monthStart dayStart hourStart [g_DSToffset hours - default is 1 if unset]",
+ //cmddetail:"descr":"Checks, if actual time is during DST or not.",
+ //cmddetail:"fn":"CLOCK_CalcDST","file":"driver/drv_ntp.c","requires":"",
+ //cmddetail:"examples":""}
+ CMD_RegisterCommand("clock_calcDST",CLOCK_CalcDST, NULL);
+#endif
addLogAdv(LOG_INFO, LOG_FEATURE_NTP, "NTP driver initialized with server=%s, offset=%d", CFG_GetNTPServer(), g_timeOffsetSeconds);
g_synced = false;
@@ -319,7 +536,12 @@ unsigned int NTP_GetCurrentTime() {
return g_ntpTime;
}
unsigned int NTP_GetCurrentTimeWithoutOffset() {
+#if ENABLE_NTP_DST
+ return g_ntpTime - g_timeOffsetSeconds - g_DST%128; // if g_DST is unset, it's -128 --> -128%128 = 0 as needed
+#else
return g_ntpTime - g_timeOffsetSeconds;
+#endif
+
}
@@ -440,10 +662,12 @@ void NTP_CheckForReceive() {
g_ntpTime = secsSince1900 - NTP_OFFSET;
g_ntpTime += g_timeOffsetSeconds;
+#if ENABLE_NTP_DST
+ g_ntpTime += setDST(0)*3600; // just to be sure: recalculate DST before setting, in case we somehow "moved back in time" we start with freshly set g_ntpTime, so don't change it inside setDST()!
+#endif
addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Unix time : %u",(unsigned int)g_ntpTime);
ltm = gmtime(&g_ntpTime);
- addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Local Time : %04d-%02d-%02d %02d:%02d:%02d",
- ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
+ addLogAdv(LOG_INFO, LOG_FEATURE_NTP, LTSTR, LTM2TIME(ltm));
if (g_synced == false) {
EventHandlers_FireEvent(CMD_EVENT_NTP_STATE, 1);
@@ -475,6 +699,13 @@ void NTP_SendRequest_BlockingMode() {
void NTP_OnEverySecond()
{
g_ntpTime++;
+#if ENABLE_NTP_DST
+ if (useDST && (g_ntpTime >= next_DST_switch_epoch)){
+ int8_t old_DST=g_DST;
+ setDST(1);
+ addLogAdv(LOG_INFO, LOG_FEATURE_NTP,"Passed DST switch time - recalculated DST offset. Was:%i - now:%i",old_DST,g_DST);
+ }
+#endif
#if ENABLE_CALENDAR_EVENTS
NTP_RunEvents(g_ntpTime, g_synced);
@@ -523,8 +754,8 @@ void NTP_AppendInformationToHTTPIndexPage(http_request_t* request)
ltm = gmtime(&g_ntpTime);
if (g_synced == true)
- hprintf255(request, "NTP (%s): Local Time: %04d-%02d-%02d %02d:%02d:%02d
",
- CFG_GetNTPServer(),ltm->tm_year+1900, ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
+ hprintf255(request, "NTP (%s): " LTSTR "
",
+ CFG_GetNTPServer(),LTM2TIME(ltm));
else
hprintf255(request, "NTP: Syncing with %s....
",CFG_GetNTPServer());
}
diff --git a/src/driver/drv_ntp.h b/src/driver/drv_ntp.h
index 611505b3e..db29f00a4 100644
--- a/src/driver/drv_ntp.h
+++ b/src/driver/drv_ntp.h
@@ -27,6 +27,13 @@ int NTP_PrintEventList();
int NTP_GetEventTime(int id);
int NTP_RemoveClockEvent(int id);
int NTP_ClearEvents();
+#if ENABLE_NTP_DST
+int Time_IsDST();
+// usually we want to set/correct g_ntpTime inside setDST() --> call setDST(1)
+// only after setting g_ntpTime freshly from an NTP packet --> call setDST(0)
+// we must not alter g_ntpTime inside setDST in this case (the old offsets are no longer valid)
+uint32_t setDST(bool setNTP);
+#endif
extern time_t g_ntpTime;
extern struct SUN_DATA { /* sunrise / sunset globals */
diff --git a/src/obk_config.h b/src/obk_config.h
index 4af7d34bb..bfcc0b304 100644
--- a/src/obk_config.h
+++ b/src/obk_config.h
@@ -24,6 +24,7 @@
// Some limited drivers are supported on W600, OBK_DISABLE_ALL_DRIVERS is not defined
#define ENABLE_TASMOTADEVICEGROUPS 1
#define ENABLE_NTP 1
+//#define ENABLE_NTP_DST 1
#define ENABLE_DRIVER_BL0937 1
#define ENABLE_DRIVER_DHT 1
#define ENABLE_TASMOTA_JSON 1
@@ -41,6 +42,7 @@
#define ENABLE_TASMOTADEVICEGROUPS 1
#define ENABLE_LITTLEFS 1
#define ENABLE_NTP 1
+#define ENABLE_NTP_DST 1
#define ENABLE_DRIVER_LED 1
#define ENABLE_DRIVER_BL0937 1
#define ENABLE_DRIVER_BL0942 1
@@ -87,6 +89,7 @@
#define ENABLE_TASMOTADEVICEGROUPS 1
#define ENABLE_LITTLEFS 1
#define ENABLE_NTP 1
+//#define ENABLE_NTP_DST 1
#define ENABLE_CALENDAR_EVENTS 1
#define ENABLE_DRIVER_LED 1
#define ENABLE_DRIVER_BL0937 1
@@ -109,6 +112,7 @@
#define ENABLE_TASMOTADEVICEGROUPS 1
#define ENABLE_LITTLEFS 1
#define ENABLE_NTP 1
+//#define ENABLE_NTP_DST 1
#define ENABLE_NTP_SUNRISE_SUNSET 1
#define ENABLE_DRIVER_LED 1
#define ENABLE_DRIVER_BL0937 1
@@ -166,6 +170,7 @@
//#define OBK_DISABLE_ALL_DRIVERS 1
#define ENABLE_TASMOTADEVICEGROUPS 1
#define ENABLE_NTP 1
+//#define ENABLE_NTP_DST 1
#define ENABLE_DRIVER_BL0937 1
#define ENABLE_DRIVER_LED 1
#define ENABLE_DRIVER_WEMO 1
@@ -182,6 +187,7 @@
#define ENABLE_I2C 1
#define ENABLE_NTP 1
+//#define ENABLE_NTP_DST 1
#define ENABLE_DRIVER_LED 1
#define ENABLE_DRIVER_TUYAMCU 1
#define ENABLE_LITTLEFS 1
diff --git a/src/selftest/selftest_local.h b/src/selftest/selftest_local.h
index 71d314d4c..c028f8286 100644
--- a/src/selftest/selftest_local.h
+++ b/src/selftest/selftest_local.h
@@ -115,6 +115,7 @@ void Test_RepeatingEvents();
void Test_HTTP_Client();
void Test_DeviceGroups();
void Test_NTP();
+void Test_NTP_DST();
void Test_NTP_SunsetSunrise();
void Test_MQTT();
void Test_Tasmota();
diff --git a/src/selftest/selftest_ntp_DST.c b/src/selftest/selftest_ntp_DST.c
new file mode 100644
index 000000000..a0a074cc0
--- /dev/null
+++ b/src/selftest/selftest_ntp_DST.c
@@ -0,0 +1,161 @@
+#ifdef WINDOWS
+
+#include "selftest_local.h"
+
+void Test_NTP_DST() {
+ // reset whole device
+ SIM_ClearOBK(0);
+
+ CMD_ExecuteCommand("startDriver NTP", 0);
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ // set Sunday, Mar 31 2024 01:59:55 CET
+ // that's 5 seconds before DST starts in Europe
+ NTP_SetSimulatedTime(1711846795);
+
+ SELFTEST_ASSERT_EXPRESSION("$minute",59);
+ SELFTEST_ASSERT_EXPRESSION("$hour", 1);
+ SELFTEST_ASSERT_EXPRESSION("$second", 55);
+ SELFTEST_ASSERT_EXPRESSION("$day", 0);
+ SELFTEST_ASSERT_EXPRESSION("$mday", 31);
+ SELFTEST_ASSERT_EXPRESSION("$month", 3);
+ SELFTEST_ASSERT_EXPRESSION("$year", 2024);
+
+
+
+ // now set DST information for Europe:
+ // DST ends last Sunday of October at 3 local (summer) time
+ // DST starts last Sunday of March at 2 local (standard) time
+ // arguments are: nthWeekEnd, monthEnd, dayEnd, hourEnd, nthWeekStart, monthStart, dayStart, hourStart
+ //
+ // 0 10 1 3 means "End of DST on last(=0) Octobers(=10) Sunday(=1) at 3"
+ // 0 3 1 2 means "Start of DST on last(=0) Marchs(=3) Sunday(=1) at 2"
+ CMD_ExecuteCommand("CLOCK_calcDST 0 10 1 3 0 3 1 2", 0);
+ // test - we are (slightly) "before" DST
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // advace 6 seconds, so we are 1 second after 2:00, hence summertime started
+ // in fact the actual time could never happen, since clock
+ // advances from 2:00:00 to 3:00:00 clock can't be 2:00:01 on this day
+ // --> NTP offset should be 2 now, we will set that later
+ Sim_RunSeconds(6, false);
+
+ // check time
+ // DST switch should have been done by system
+
+ SELFTEST_ASSERT_EXPRESSION("$hour", 3);
+ SELFTEST_ASSERT_EXPRESSION("$minute",00);
+ SELFTEST_ASSERT_EXPRESSION("$second", 01);
+ // test, that we are _in_ DST now
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+
+ // set Sunday, Oct 27 2024 02:59:55 CEST
+ // it's summertime now (for another 5 seconds)
+ // because that's 5 seconds before DST ends in Europe
+ NTP_SetSimulatedTime(1729990795);
+
+ SELFTEST_ASSERT_EXPRESSION("$minute",59);
+ SELFTEST_ASSERT_EXPRESSION("$hour", 2);
+ SELFTEST_ASSERT_EXPRESSION("$second", 55);
+ SELFTEST_ASSERT_EXPRESSION("$day", 0);
+ SELFTEST_ASSERT_EXPRESSION("$mday", 27);
+ SELFTEST_ASSERT_EXPRESSION("$month", 10);
+ SELFTEST_ASSERT_EXPRESSION("$year", 2024);
+ // test, that we are still _in_ DST now
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+
+ // advace 6 seconds, so we are 1 second after 3:00, hence summertime ended
+ Sim_RunSeconds(6, false);
+
+ // check time - DST switch should have hapened, so clock was
+ // turned back for 1 hour
+ SELFTEST_ASSERT_EXPRESSION("$hour", 2);
+ SELFTEST_ASSERT_EXPRESSION("$minute",00);
+ SELFTEST_ASSERT_EXPRESSION("$second", 01);
+ // test, that we are _not_ in DST any more
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // some more tests with directly setting the ntp time
+ // just checking DST function ...
+
+ // 1743296395 = Sun, Mar 30 2025 01:59:55 CET
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ NTP_SetSimulatedTime(1743296395);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ // 1761440395 = Sun, Oct 26 2025 02:59:55 CEST
+// CMD_ExecuteCommand("ntp_timeZoneOfs 2", 0);
+ NTP_SetSimulatedTime(1761440395);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // 1774745995 = Sun, Mar 29 2026 01:59:55 CET
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ NTP_SetSimulatedTime(1774745995);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ // 1792889995 = Sun, Oct 25 2026 02:59:55 CEST
+// CMD_ExecuteCommand("ntp_timeZoneOfs 2", 0);
+ NTP_SetSimulatedTime(1792889995);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // 1806195595 = Sun, Mar 28 2027 01:59:55 CET
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ NTP_SetSimulatedTime(1806195595);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ // 1824944395 = Sun, Oct 31 2027 02:59:55 CEST
+// CMD_ExecuteCommand("ntp_timeZoneOfs 2", 0);
+ NTP_SetSimulatedTime(1824944395);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // 1837645195 = Sun, Mar 26 2028 01:59:55 CET
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ NTP_SetSimulatedTime(1837645195);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ // 1856393995 = Sun, Oct 29 2028 02:59:55 CEST
+// CMD_ExecuteCommand("ntp_timeZoneOfs 2", 0);
+ NTP_SetSimulatedTime(1856393995);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // 1869094795 = Sun, Mar 25 2029 01:59:55 CET
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ NTP_SetSimulatedTime(1869094795);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ // 1887843595 = Sun, Oct 28 2029 02:59:55 CEST
+// CMD_ExecuteCommand("ntp_timeZoneOfs 2", 0);
+ NTP_SetSimulatedTime(1887843595);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+ // 1901149195 = Sun, Mar 31 2030 01:59:55 CET
+ CMD_ExecuteCommand("ntp_timeZoneOfs 1", 0);
+ NTP_SetSimulatedTime(1901149195);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ // 1919293195 = Sun, Oct 27 2030 02:59:55 CEST
+// CMD_ExecuteCommand("ntp_timeZoneOfs 2", 0);
+ NTP_SetSimulatedTime(1919293195);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 1);
+ Sim_RunSeconds(6, false);
+ SELFTEST_ASSERT_EXPRESSION("$isDST", 0);
+
+}
+
+
+#endif
diff --git a/src/win_main.c b/src/win_main.c
index 695fc4a11..9d364d0a7 100644
--- a/src/win_main.c
+++ b/src/win_main.c
@@ -178,6 +178,7 @@ void Win_DoUnitTests() {
Test_DHT();
Test_Tasmota();
Test_NTP();
+ Test_NTP_DST();
Test_NTP_SunsetSunrise();
Test_HTTP_Client();
Test_ExpandConstant();