ESP8266 time.h - convert a UTC tm object to a UTC time_t ?

ESP8266 time() etc.

TL;DR : Is there a reliable and robust timezone/DST-neutral way of converting a UTC time in a tm struct to a time_t UTC Unix epoch date. That is an equivalent of the timegm() extension found on some C/C++ platforms. ?

The details:

I'm attempting to update an old project which uses an ESP8266, NTP and a DS3231 RTC module. It uses various Arduino libraries including the TimeZone library. The ESP8266 Arduino platform now has built in functions for obtaining an NTP timestamp and updating the ESP8266 system time, so theoretically, this should now be a simple task.

I run the RTC in UTC and the RTC internally requires the date to be in "broken down time" format. That is day, month, year, hour, minute, second instead of a Unix time stamp.

That is no problem. There is a standard C++ time library function gmtime(). It takes the current time (UTC) and updates a struct of type tm. That is all standard C/C++ stuff:

time_t timeNowUTC = time( nullptr ) ;
tm timeStruc ; // broken down time
gmtime_r( &timeNowUTC, &timeStruc ) ;

So, with timeStruct, I can set the time on the RTC having obtained the "broken down" time format of the current UTC time, and maybe applying a few corrections to compensate for months sometimes being enumerated as a range 0 to 11 and at other times 1 to 12, different epochs etc. etc.

The problem now comes when reading the RTC because, in the standard C++ time library, there is no reverse of the gmtime() method of converting a "broken down" time into a UTC Unix time stamp. The nearest equivalent is mktime() but that is timezone aware and, in this case, does an unwanted conversion to the local time zone. Many C++ implementations have a non standard extension timegm() for doing just that. Not, however, the ESP8266.

Apparently, the GNU C Library documentation recommends the following equivalent, which effectively sets the time zone to UTC, uses the standard mktime, then reverts the timezone data to its original value ;

#include <time.h>
#include <stdlib.h>

time_t
my_timegm(struct tm *tm)
{
     time_t ret;
     char *tz;

     tz = getenv("TZ");
     if (tz)
         tz = strdup(tz);
     setenv("TZ", "", 1);
     tzset();
     ret = mktime(tm);
     if (tz) {
         setenv("TZ", tz, 1);
         free(tz);
     } else
         unsetenv("TZ");
     tzset();
     return ret;
}

The problem is that that does not work with the ESP8266 Arduino core. The combination of setenv("TZ", "", 1) and tzset() does not prevent mktime() from applying an unwanted time zone skew to the returned result.

However, the ESP8266 Arduino time.h comes with something similar, namely configTime(). But there are 2 signatures for it, an "old" format and a "new" format.

The new format of configtime() is as simple as this: configTime( "" , "ch.pool.ntp.org" ) where the first argument is the timezone, in this case, the empty string which indicates UTC but could also be something like "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" when setting the time zone (and DST rules).
The problem, in this case, is that the new format signature also fails to clean out the current time zone / DST rules and revert to UTC.

The old format of configtime() that is: configTime( 0 , 0, "ch.pool.ntp.org" . . . ) where the first and second arguments are timezone offset Hours and DST respectively does, however, work in this case.

The simple solution would be to use the old format of configtime(). However, the risk is that I will be relying on some bizarre side effects of some "old" function which may be changed at some later date.

Is there another way of achieving the same thing, namely a robust, timezone/DST-neutral way of converting a tm struct to a UTC unix time stamp?

I've included a test sketch (a slightly bloated MCVE). 5 constants have to be set at the top of the sketch to demonstrate the failure and success conditions. It simply takes a time_t UTC time value, converts it into a tm struct, then un-converts it back to a UTC time_t value with varying degress of success.

I'll probably open an issue at some stage but that would be unlikely to solve my immediate problem.

ConfigTimeErrorDemo.ino (6.17 KB)

I only read the TL;DR version of your question, but does makeTime() from the TimeLib library do what you need?

Of course, the TimeLib.h function makeTime() is similar to the missing timegm(), but it reads a struct of type tmElements_t where the epoch is different to that in struct tm, the days of the week are enumerated differently etc. etc.

Also, I don't want to now mix Arduino TimeLib functions with with the Expressif / ESP8266 C++ native time functions if I can avoid it since the main reason for using these is the integrated time zone / DST support which, if it works, is very convenient.

In the meantime, I have reported the issue: configTime() new signature does not influence mktime() under the tested circumstances · Issue #7806 · esp8266/Arduino · GitHub

title: configTime() new signature does not influence mktime() under the tested circumstances #7806

Hi,
I'm using wired conversion with localtime, gmtime and mktime to achieve UTC timestamp from tm structure filled with UTC time and with not set DST (tm_isdst) flag.

Configure properly esp8266 time system to set your time zone, example:

  configTime(TZ_Europe_Zurich, "pool.ntp.org");

and I have written following function:

time_t tmUTC_to_local(tm * tm) {
  time_t tStampBadLocaltime = mktime(tm);

  struct tm tmUTC;
  struct tm tmLocaltime;
  gmtime_r(&tStampBadLocaltime, &tmUTC);
  localtime_r(&tStampBadLocaltime, &tmLocaltime);
  time_t tstampBadUTC = mktime(&tmUTC);
  time_t tstampLocaltime = mktime(&tmLocaltime);
  time_t tLocalOffset = tstampLocaltime - tstampBadUTC;
  return tStampBadLocaltime + tLocalOffset;
}

Function calculates UTC timestamp at winter and summer time properly. I tested it for my European time.

Example test to test tm-UTC timestamp conversion without needed access to NTP server:

void setup() {
  Serial.begin(9600);
  Serial.println("\nTime testing is starting...");

  const char timeFormat[] = "%Y-%m-%d %X";

  char timeBufText[32];

  const struct timeval TimeValWinter = {1610833262, 0}; // GMT: Saturday, 16 January 2021 21:41:02
  const struct timeval TimeValSummer = {1623879662, 0}; // GMT: Wednesday, 16 June 2021 21:41:02
  
  configTime(TZ_Europe_Zurich, "ll");
  settimeofday(&TimeValWinter, NULL);
  delay(3500); // to be sure the clock is ticking

  time_t ttWinter;
  time(&ttWinter);

  Serial.print("\Winter timestamp: ");
  Serial.println(ttWinter);

  struct tm tmWinterUTC;
  gmtime_r(&ttWinter, &tmWinterUTC);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmWinterUTC);
  Serial.print("Winter GMT time: ");
  Serial.println(timeBufText);
  
  struct tm tmWinterLOCAL;
  localtime_r(&ttWinter, &tmWinterLOCAL);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmWinterLOCAL);
  Serial.print("Winter Local time: ");
  Serial.println(timeBufText);

  // -------------------------------------
  time_t ttUTCwinter = tmUTC_to_local(&tmWinterUTC);
  // -------------------------------------

  struct tm tmWinterUTCconverted;
  gmtime_r(&ttUTCwinter, &tmWinterUTCconverted);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmWinterUTCconverted);
  Serial.print("Winter GMT time after conversion: ");
  Serial.println(timeBufText);
  
  struct tm tmWinterLOCALconverted;
  localtime_r(&ttUTCwinter, &tmWinterLOCALconverted);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmWinterLOCALconverted);
  Serial.print("Winter Local time after conversion:");
  Serial.println(timeBufText);

  // --------------------------
  // testing summer (DST) time

  settimeofday(&TimeValSummer, NULL);
  delay(3500); // to be sure the clock is ticking

  time_t ttSummer;
  time(&ttSummer);

  Serial.print("\nSummer timestamp: ");
  Serial.println(ttSummer);

  struct tm tmSummerUTC;
  gmtime_r(&ttSummer, &tmSummerUTC);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmSummerUTC);
  Serial.print("Summer GMT time: ");
  Serial.println(timeBufText);
  
  struct tm tmSummerLOCAL;
  localtime_r(&ttSummer, &tmSummerLOCAL);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmSummerLOCAL);
  Serial.print("Summer Local time: ");
  Serial.println(timeBufText);

  // -------------------------------------
  time_t ttUTCsummer = tmUTC_to_local(&tmSummerUTC);
  // -------------------------------------

  struct tm tmSummerUTCconverted;
  gmtime_r(&ttUTCsummer, &tmSummerUTCconverted);
  strftime(timeBufText, sizeof(timeBufText), timeFormat, &tmSummerUTCconverted);
  Serial.print("Summer GMT time after conversion: ");
  Serial.println(timeBufText);
  
  struct tm tmSummerLOCALconverted;
  localtime_r(&ttUTCsummer, &tmSummerLOCALconverted);
  strftime(timeBufText, sizeof(timeBufText), "%Y-%m-%d.%X", &tmSummerLOCALconverted);
  Serial.print("Summer Local time after conversion:");
  Serial.println(timeBufText);
}

void loop() {}

Regards.

OK. Thanks. In the meantime I've solved my immediate problem with an own version of timegm(). This does a neutral (UTC) conversion from a tm struct to a time_t value, that is without any skewing for timezone or DST settings.
I've added it to the Incident report I mentioned previously: configTime() new signature does not influence mktime() under the tested circumstances · Issue #7806 · esp8266/Arduino · GitHub

I'll look at the whole thing again, once I have solved another problem with the ESP / C++ time library software. That is changing the NTP update/refresh interval is delivering erratic results.

I've had another look at the original problem which I reported. I have now closed the issue because the main problem was due to my incorrect usage of configTime( ) which failed (silently) to force the time zone to UTC because of the way I specified the UTC time zone.

So it looks like there are now three methods of getting from time_t --> struct tm --> time_t (within UTC) for example, for setting and recovering a time from an RTC.

  1. a timegm() type routine for the struct tm --> time_t conversion.
  2. using configTime() to temporarily set the time zone to UTC while doing struct tm --> time_t using mktime(), which is what I originally attempted.
    or
  3. Your method of deriving the UTC offset for the current time zone, which is similar in concept to the way the Arduino TimeZone library is used.

I added a complete working sketch to the incident report before closing it.
I also found the example here quite useful for using the time zone features without the sntp environment running: Arduino/NTP-TZ-DST.ino at master · esp8266/Arduino · GitHub . I'm currently working on project where I want to failover to an RTC if the timestamp from NTP goes stale (loss of WLAN etc.) and fails back if the NTP is restored, hence my interest in this issue.

I'm not quite understanding what you are really wanting or trying to do and why the existing tools/APIs are not good enough.
time_t values are universal and are not affected by local timezone rules or offsets.
mktime() converts a tm to a time_t and gmtime() converts from a time_t to a tm
Why would you need anything else if you are storing UTC time values in your RTC?
You can take a time_t value and break it down with gmtime() and store the broken down values in the RTC.
You fetch the values from the RTC to build a tm structure then call mktime() to get the time_t which can be used to fill in a timeval which can be passed into settimeofday() to set the time in time library code.
You can also use the esp time library without using NTP and with or without an RTC.

Two big upcoming issues that many people are not thinking about yet are issues that can cause code to break in 2036 and 2038. NTP can have issues in 2036 depending on how the initial time is set and 32 bit time_t values roll over and break in 2038.

What library you use and how you choose to use it, can depend on what is most important to you as most of them have issues that in some cases can be a deal breaker.

On a clock project that I wanted to last beyond 2038 and be very robust, I ended up with a hybrid solution using multiple libraries and methodologies.
And while, IMO, it is ugly, it was pretty much the only way to get to what I wanted for this particular project which was a clock for my mother who has dementia.
It has to "just work" and I wanted it to continue to work beyond 2038, perhaps with some reduced functionality, even if I can't ever update the code or say in the future WiFi as we know it goes away.
If the network is up and working, It can autoset itself (and the RTC) and support local DST changes when displaying the time - with ZERO input from the user( other than the WiFi configuration) using a time server that provides current local time.
If the network is down, it falls back to an RTC,
If the RTC is not working, fails or is not present, it reverts back to software based time tracking.
User can manually configure the time which also sets the RTC if working / present.
The manually configured time is corrected / updated if the network is up and working.

PM me and I can cover things in more detail if you have interest.


Having dealt with and actually worked with several time libraries and their authors correct various issues in libraries including: AVR libc, TimeLib, TimeZone, ezTime, esp8266, esp32, STM, and having implemented several clock projects working around issues with all of them I can offer some comments based on my experience.

Here are come high level comments on them:

#1 Most of these libraries have issues, some very serious

#2 many people do not understand how time_t values work and how local time is created from them.
they incorrectly assume that the time_t value represents the local time so it varies depending on the timezone and dst offset. This is incorrect.
The TimeZone library makes this incorrect assumption and I have had long discussions with Jack about this.
The ezTime library also makes this incorrect assumption and I've also had some discussions with the author of that library.

#3 Many of these libraries suffer from the Y2k38 issue which means the code breaks Jan 2038
This is now only 17 years away.

Here are a few notable things about a each of the libraries I've worked with

  • AVR libC code
    This code avoids the Y2K38 by changing the time_t value to an unsigned 32 bit value.
    This pushes the rollover to 2106.
    This library also altered the time_t epoch from 1970 to 2000
    While this buys another 30 years it means the time_t values from this library are not directly compatible with "normal" time_t values.

  • Time/TimeLib
    Michael Margolis lives in the London area which is why it doesn't support timezones.
    One good thing about this library is that it uses an unsigned 32 bit time_t value so the rollover is not until 2106.
    It does not support local timezones.
    It does have the ability to setup a sync provider to update the time based on some external source.
    Integrating that library with multiple external sources can be a bit challenging depending on what they are and how they work.

  • TimeZone
    Jack's library is an addon for Time/TimeLib to provide local time and DST offset support.
    It does work quite well but it was done without the understanding of what a time_t really represents and how they are supposed work.
    i.e. it depends on using munged time_t values.
    He and I have talked about this and he now has a better understanding of time_t values.

  • ezTime
    This library was intended to replace Time/TimeLib and to be the be all / end all of time libraries for Arduino.
    It does have the potential to meet this goal. However, as it is now it is falls VERY short.
    The issues with this are related to how it was implemented. Again by not understanding how time_t values work.
    The main problem with it now is that some of the low level code was missing some parameters so the code made assumptions about certain uses when converting time_t values form "normal" time_t to munged time_t values and it screws up on some very common use cases. The library also does lots of expensive back and forth calculations on each time & time tick update since it attempts to maintain time as a local broken down time vs a simple UTC epock time_t value.

If ezTime library were re-written to always use a proper time_t value, and corrected how the time_t was tracked and updated, the code would get much simpler, faster and the API could be simplified all while still maintining full Time/TimeLib compatibility.
It could also transparently provide some very useful functions like the ability to set the time using a time_t as well as broken down time components based on GMT time or the currently selected local time.
This library has great promise but needs a re-work.
I could do the re-work, but I didn't want to. I may have to revisit this and just do it.
It could offer a complete self contained portable Arduino time library that is TimeLib compatible and could offer everything related to time handling.

  • esp8266 & ESP32
    These both provide a fairly good level of unix time library support but they do have some API differences between them with respect to configuration and some capabilities.
    example: settimeofday_cb() does not exist on ESP32.
    This can be a deal breaker for an application that may want to use the callback to set an RTC on each time update.

esp8266 and ESP32 use different names for configuring the time library.
esp8266 uses configTime(TZ, ntpserver, ...) and ESP32 uses configTzTime(TZ, ntpserver, ...)

Both libraries currently use signed 32 bit values for time_t so both with break in 2038.

  • esp8266
    The esp8266 Arduino core comes with unix "time" library support.
    There have been some MAJOR issues with it and still are some issues.
    The biggest issue was again some misunderstanding about time_t and how local offsets and timezones work.
    In this case some of the code related to timezone offsets was being handled down in the ntp code which is incorrect.
    The net result was that using the configTime() with the offets and trying to use an offset as a timezone offset REALLY breaks things.
    i.e. if you attempt to use the offset or dst flag in
configTime(tzoffset, dstflg, "ntp-server1", "ntp-server2", "ntp-server3");

The time library code will not handle local and utc/gmtimes correctly.
The time library code does work correctly if using the TZ variable. This is because the tzoffset and dslflg is handled by the NTP code which does things incorrectly whereas the TZ variable is handled by all the gnu time library code which does it correctly.
The safest thing to do is to use the more recent version of the configTime where you specify the TZ string and a NTP server:

configTime (TZstr, "pool.ntp.org");

But that doesn't exist on ESP32 where it has been renamed as configTzTime() instead of creating an overload.

STM:
This is a bit of mess, since there is lots of confusion of which core is from STM vs which is Roger Clarks core given the overlapping names of the platform cores and web site that use to be for Roger's core that has now been taken over to support the STM core.
The STM core uses 64 bit time_t values.

bperrybap:
I'm not quite understanding what you are really wanting or trying to do and why the existing tools/APIs are not good enough.
time_t values are universal and are not affected by local timezone rules or offsets.
mktime() converts a tm to a time_t and gmtime() converts from a time_t to a tm
Why would you need anything else if you are storing UTC time values in your RTC?
You can take a time_t value and break it down with gmtime() and store the broken down values in the RTC.
You fetch the values from the RTC to build a tm structure then call mktime() to get the time_t which can be used to fill in a timeval which can be passed into settimeofday() to set the time in time library code.

You must have missed the significance of the first point I made in my OP:

TL;DR : Is there a reliable and robust timezone/DST-neutral way of converting a UTC time in a tm struct to a time_t UTC Unix epoch date. That is an equivalent of the timegm() extension found on some C/C++ platforms. ?

and not understood why mktime() cannot be used directly in this situation. I'll try to explain it like this:

  1. I want to to go through this chain for storing, and possibly recovering a time, in UTC, on an RTC module:
time_t(UTC) ---> tm struct (UTC) on RTC ---> time_t(UTC)

and clearly without any timezone / DST skew at any point in the chain.

  1. For the first part, namely, time_t(UTC) ---> tm struct (UTC), there are two C++ time.h library functions for converting a time_t variable to a tm struct (broken down time) namely gmtime() and localtime(). gmtime() does the conversion without a timezone/DST skew. localtime() does this with a timezone/DST skew. However, in the standard C++ time.h library, there is only one function, mktime(), for getting back from a tm struct to a time_t variable. Clearly then, mktime() cannot be used in combination with both localtime() and gmtime() since one applies a timezone skew and the other does not. To get around this, some platforms add an extension function timegm() which does not apply a timezone skew. mktime() does apply a time zone skew.

  2. To summarise:

time_t ---> localtime() ---> tm struct ---> mktime() ---> time_t

would work, but the intermediate value in tm would be in local time, which I do not want.

time_t ---> gmtime() ---> tm struct ---> timegm() ---> time_t

would work, would also leave the intermediate results in UTC, but timegm() has not been implemented on this ESP platform.

  1. My problem started with the GNU recommended method of getting around the problem of the missing timegm() functionality, namely switching the timezone settings to UTC, using mktime(), then restoring the local timezone settings afterwards:
time_t ---> gmtime() ---> tm struct ---> [force timezone to UTC]  mktime() ---> time_t [restore timezone]

In the meantime, I have solved the problem by creating a version of timegm().

As for the rest of your post, I agree that there is a huge mess out there with historical relics, missing functionality, different epoch dates, different enumeration of days of the week, different timezone representations, vendor additions etc. etc. and it is bound to get worse as more changes are made in attempt to avert problems for the approaching Unix time Dday and the NTP time Dday.

This is the best overview of the current situation that I can find: Time, Clock, and Calendar Programming In C . For exactly the points I've mentioned above, see the section marked gmtime(3) and localtime(3) and its following section.

I just realized my reason for not understanding the issue.

Apparently, I'm too old with very old memories of how things used to work.
I still have Unix programming manuals that I got nearly 40 years from ATT Bell labs from basically the old Unix SYS III days.
(published in 1979)
Back then, mktime() didn't even exist; heck even the data type time_t didn't' exist yet.
In the early to mid 80's POSIX didn't exist so there were often portability issues on various platforms between various OS vendors.
At some point in the 80s mktime() was added and back then it did not compensate for timezones, it basically assumed the tm struct represented gmtime.
I remember having to write some time code back in the 80s and it was difficult to get form a local time to a proper time_t since you had to do a bunch of things yourself.
In fact back then there was no network time syncing and the time and timezone offset was set by the operator.
Typically a cron job patched a file to set the DST offset twice a year.

Now it appears that mktime() takes the tm structure and reverses any local timezone rules that were applied to the values when generating the time_t value.
And apparently, I never got the memo. Guess I've been living under a rock......

Also, apparently, over the years, I've been using some reduced functionality versions of time libraries on various embedded platforms where mktime() worked like I was accustomed to from decades ago by not being affected by any local timezone rules.

So that is why I was stumped as to why you couldn't easily get back and forth from a time_t to a tm and back again using the functions time(), gmtime(), and mktime()

--- bill

The situation has not get any better since the days you mentioned. In fact, it has got worse, because all that stuff is still there to be maintained and, in the meantime, a lot more has been added. Further, there is plenty more scope to make it even worse in preparation for the ends of the epochs.

It is a pity really, because the simple task of getting a timestamp from an NTP server, applying a constant to it to get a Unix time stamp, interpreting the local time zone setting to determine the time zone offset with respect to UTC (another constant to apply) then checking if a further daylight saving time offset has to be applied, has given rise to probably millions of lines of code.

For my mother's clock project, I ended opting to use TimeLib for the time tracking as the current esp8266 time library breaks in 2038 and when I started the project, there were some severe issues in the esp8266 code that made it unusable for me.

I ended up with a little bit of an odd combination of things that ended up working out pretty well.

I store the time in the RTC in local time rather than GMT and use the RTC as sync provider for TimeLib.
I abuse the API by using munged up time_t values rather than proper time_t values.
I really didn't like doing this but it makes a lot of things simpler and worked better than trying to do it the way it should be done.

Rather than NTP I use worldtimeapi.org to get the local time based on IP address and use that to set the RTC on powerup/reset and once every day at 02:00:15 (the daily sync to worldtimeapi will catch the DST changes)
I parse the JSON output to get the needed fields.
I only turn on the WiFi radio during the worldtimiapi sync so it is off and not attached to the network most of the time.

Buttons can also set the time. When the time is changed, it sets the RTC and the TimeLib time.
The RTC library (DS3231) can get/set the time using a time_t which makes things easy.
By abusing the API to use munged time_t values to represent local time everything gets simpler.
You get a time_t value from the DS3231 library which is not correct but rather represents a time_t value that can be given directly to TimeLib which will cause it to present the current local time using the TimeLib APIs.
To set the RTC you pass in this same munged time_t which makes things really simple.
To get from the local time values presented by worldtimeapi.org I use the TimeLib function makeTime(tm) which takes the local time values and creates a munged time_t that can be given to the RTC library to set the RTC.

Yeah, having done lots of unix time code over the years, it initially really bothered me to do it this way, but given my requirements, I didn't have much choice, other than using ezTime which I initially tried but it was too broken to use.
In the end, it ended up being fairly trivial to implement it this way and it is supercool to have it autoset itself, with no messing with timezone configuration.

--- bill

I've built a number of clocks each using one of a number of time sources: NTP, Satellite, Radio time (DCF77) and autonomous (manually set or synchronised over IR). In all cases, the Arduino system time is UTC. If an RTC is present, then that also runs in UTC. I've used the Jack Christensen Time zone library (lightly hacked) and have used the feature of TimeLib.h where you specify a "time_t" format variable to "bend" the UTC to local time for display purposes. For me, that it no less "pure" than using say the C++ time.h function localttime() to get a local time representation from a UTC time source, so it doesn't bother me. I can cut a large amount of code from my existing design be moving to the ESP8266 native NTP support, once I have developed at least a good level of confidence in it.

I'm currently developing (actually re-developing) an NTP time switch. The time switch has the special feature that it is sunrise(SR)/sunset(SS) time aware, so the user has the option of entering a code SR or SS in place of a normal time such as "07:15" if required. I want it to be very robust and continue completely autonomously, even if the internet connection fails long term (say the WLAN password is changed). Obviously, once the RTC backup battery expires, that is the end of it at the next power fail restart.

When it is finished and documented, I'll be publishing it in the Exhibition Gallery here where I have already a number of exhibits.

I'm currently developing (actually re-developing) an NTP time switch. The time switch has the special feature that it is sunrise(SR)/sunset(SS) time aware, so the user has the option of entering a code SR or SS in place of a normal time such as "07:15" if required

I've developed roller blind manager. I'm using https://sunrise-sunset.org/api to get sunrise and sunset times. But JSON "Sunset and sunrise times API" provides GMT time in a text form iso8601, so I have to convert time for my locally time worked esp as described in my previous post.
I think it was not needed effort. The esp could work in GMT time and get the sunrise and sunset in GMT time, and

so on it not depends on DST.

In my case, I am using the Arduino TimeLord library GitHub - probonopd/TimeLord: Arduino library with many time and date related functions... Daylight Savings, Moon Phase, Sidereal time, with the SunRise library rolled in. to determine sunrise and sunset times. I run it in UTC, although it is also possible also to feed it timezone/DST settings (in yet another format). I have to convert the results to local time because I am treating these the same as other times which have been entered by the user which are (naturally enough) in local time.

I did an experiment once entering an array of the sunrise/sunset UTC times at my location for the 22nd day (~= equinox) in each month and interpolating between them for the intermediate days. These values do not change significantly from year to year. I seem to remember that the maximum error was about 12 minutes.

Roller blinds (or indeed a time switch for controlling a light) can be cases where daylight saving time changes are not relevant and, in fact, are a nuisance.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.