I meant that measuring elapsed seconds since the event of midnight rolling over is bad. Calculating the second-of-day number from hour,min,sec of the alarm time and comparing it to the seconds-of-day calculated from the hour,minute,sec of the current clock time is OK. For a 02:30 alarm in the US, you would have double alarms in fall as it sets the clock back from 03:00 to 02:00 and no alarm in the spring as it skips from 02:00 to 03:00.
...but how would a 01:00 alarm be handled during a DST change? 11 hours before the noon of the day? 1 hour after the midnight preceding the noon? 02:30?
I run a FORTRAN ocean model at precisely 02:34Z daily on a Linux system using a cron job scheduled in local time, drawing input data from systems working in UTC, LocalStandardTime (no DST change, but local offset), GPS time, plus another automated system that runs in a combination of Excel time, decimal julian day of year, Matlab time, and several flavors of python time. DST Time math really sucks.
OP here - the 8 times per day wasn't based on any technical requirement... Just a random guess of how often NTP synchronization would be necessary to keep time sufficiently accurate so the alarms trigger as close to actual time as possible.
Hourly (24 times per day) might be easier to implement? Edit - I guess that means 23 to 25 times with DST?
Looks like it wouldn't match people's watches at the start of a 07:00 work shift on a DST change day, counting them either 25 minutes early or 25 minutes late.
So far the code in the first post has been running since I created this thread and has been more accurate than the vintage digital timer it replaced. Though I do note that the time drifts by a few seconds on occasion - I am chalking that up to the ESP32 internal clock not sufficiently accurate without more frequent NTP synchronization. I may test modifying the NTP interval to an hourly update.
I continue to test all the fantastic suggestions posted here - no doubt some form of numeric schedule is preferrable to the char string comparison I originally came up with.
There's a lot of trickery in NTP implementations to smoothly coordinate the system time ticking along with the NTP updates. As the system figures out that the system-millis()-type clock runs x% fast or slow, it adjusts how many system- millis() are in a NTP second, and smoothly and monotonically adjusts the NTP times so they match.
If your system clock isn't insanely bad, I would doubt that you'd notice a difference between 3-hourly and hourly updates.
I am relatively new to the ESP32 platform, so I do not have any foundation to form expectations to accuracy. I am using an M5Stack DinMeter for this project and it seems relatively well made - certainly nothing that screams 'cheap'. Yet as I mentioned the clock does drift by about 3 or more seconds in that 3 hour period - I would expect better, but I have certainly observed much worse with different controllers (especially those that use internal oscillators vs external crystals) - the Arduino Due comes to mind as possibly the worst RTC clock I have ever used... why they did not choose to implement the 32KHz crystal option, I do not understand.
If the RTC has DST built in to its higher level reports, then except for scheduling for the time around 2:00 AM, shouldn't everything else be perfectly up do date automatically?
Like I said, 7:30 is a time. It will come late or early to your expectations but it will be correct.
What does one have to do with the other? The synch interval is set with sntp_set_sync_interval() and once an hour is more than sufficient, but what does that have to do with how many times a day you ring a bell?
My bad - I misread the comments and interpreted them as how frequently I would synchronize with NTP... I believe that I originally thought every 3 hours would suffice, but the time does drift within that interval.
You can take advantage of that built-in trickery even without a network connection or NTP. Say you have an external RTC or GPS time. Then adjtime() will give you the same gradual, monotonic adjustments based on the time standard you have.
I'm not sure what the technically correct answer would be - many years ago I worked night shift and we would be paid 9 hours in the fall and 7 hours in the spring (the owner was too cheap to cover the difference).
I suspected that something wasn't working as expected when I observed the 3 second drift over 3 hours update interval. Given all the other cleverness going on, I suspect that the ntp code must feature some kind of self-correcting process that modifies the ticks per millisec or something.
Most of the NTP examples I found on the web are very similar to what I posted - in fact few include the esp-sntp.h library or other features of which you refer. I have been going through the ESP32 time api documentation, but it's a lot to wrap my head around.
I would have theories about how a smart alarm clock might handle 1:30 and 2:30 AM. I would also be prepared to be surprised about what it actually did.
Hourly would be based on multiples of 3600 seconds, without regard to the day. This would work entirely as expected, DST or not. What you don't want is to present a UI with 24 checkboxes or simply calculate 24 offsets each day, which would not apply correctly on days with 23 or 25 hours.
If you did every two hours, then you might during Standard Time fire on the even hours; then switching to Daylight Time, fire on the odd hours (or vice versa). Would that be confusing? Regardless, it would be correct.
If the schedule is instead more accurately N times per day on a schedule expected by humans, then you might move the stretch/squeeze window from midnight-to-noon to 00:00 to 06:00 perhaps, when no person is really paying attention.
Just as an aside, in a related thread user gfvalvo provided some test code and I have confirmed that the ESP32 time functions DO account for DST, even without NTP updates as shown below. After synching with NTP, I manually set the time for 11/2/25 01:59 which is a minute before DST ends in my time zone - and the ESP32 clock dutifully transition from 01:59:59 to 01:00:00 as it should.
Good to know and saves a lot of DST logic coding
EDIT - I wonder if setting a boolean flag tracking if DST transitions have occurred would help towards preventing missed or duplicated hourly events at that hour.
09:18:00.656 -> Local Time: Sunday, November 02 2025 01:59:59 EDT
09:18:00.656 -> Sync Status: Reset, Remaining Time to Adjust: 0us
09:18:00.656 ->
09:18:01.612 -> Local Time: Sunday, November 02 2025 01:00:00 EST
09:18:01.612 -> Sync Status: Reset, Remaining Time to Adjust: 0us
09:18:01.612 ->
09:18:02.648 -> Local Time: Sunday, November 02 2025 01:00:01 EST
09:18:02.648 -> Sync Status: Reset, Remaining Time to Adjust: 0us