Help me understand converting between tm and time_t

I am struggling with converting between a time structure tm and time_t variable. I've been through a bunch of posts but haven't yet figured out what I am doing wrong.

The following code running on ESP32 produces the following response:

base_time date: 11-12-2024 13:14:15
new_time date: 7-8-2019 18:38:31

Why the difference?

#include "TimeLib.h"

void setup() {
  Serial.begin(9600);
  delay(500);
  Serial.println();

  time_t new_time;
  tm base_time = {0};
  char temp_ch[80];

  base_time.tm_year = 2024;
  base_time.tm_mon = 11;
  base_time.tm_mday = 12;
  base_time.tm_hour = 13;
  base_time.tm_min = 14;
  base_time.tm_sec = 15;
  base_time.tm_isdst = -1;

  new_time = mktime(&base_time);

  sprintf(temp_ch, "base_time date: %d-%d-%d   %d:%d:%d", base_time.tm_mon, base_time.tm_mday, base_time.tm_year, base_time.tm_hour, base_time.tm_min, base_time.tm_sec);
  Serial.println(temp_ch);
  sprintf(temp_ch, " new_time date: %d-%d-%d   %d:%d:%d", month(new_time), day(new_time), year(new_time), hour(new_time), minute(new_time), second(new_time));
  Serial.println(temp_ch);
}

void loop() {
}

From the top of my head I think that tm_year should be the number of years since 1900 (e.g., for the year 2024, tm_year should be 2024 - 1900 = 124). And tm_mon is zero-based, so January = 0, February = 1, …, December = 11. For November, tm_mon should be 10.

1 Like

What is that line supposed to do? "tm" is not defined in the version of TimeLib.h that I use.

Beware of the different epochs used in unix timestamps.

1 Like

The ESP32 uses the standard C time library, the posted code is mixing references to that library with references to the Arduino Time library, which is not completely compatible.

1 Like

Thanks, that explains the problem.

Ah, thanks, I didn't realize I was using struct tm from the standard C time lib and time_t from the Arduino TimeLib.h.

What I really want to do is assign values to time_t variables so I can compare and get time differences. The following works, but it seems like the wrong way to do it.

#include "TimeLib.h"

void setup() {
  Serial.begin(9600);
  delay(500);

  time_t new_time = now();
  char temp_ch[80];

  setTime(15, 14, 13, 12, 11, 2024);
  new_time = now();

  sprintf(temp_ch, " new_time date: %d-%d-%d   %d:%d:%d", month(new_time), day(new_time), year(new_time), hour(new_time), minute(new_time), second(new_time));
  Serial.println();
}

void loop() {
}

Is there a better way to assign a value to new_time?

FWIW the "base_time = {0}" was because some values in the struct weren't initializing to zero. After adding that, all members of the struct read zero so at least I could start with a known value.

Yes. First, since you're using an ESP32, get rid of 'TimeLib.h' all together and use the C time functions that are already built in. The below code first sets the time zone then builds a local time of 15:00:00 on November 17, 2024. It then converts that to an Epoch time. Then the Epoch time is converted back to a local time using the localtime() function and printed. As expected, the original local date / time is is printed.

Then the code repeats the above with local time 16:00:00 on the same day.

Finally, the two computed Epoch Times are subtracted showing a difference of 3600 seconds (1 hour), as expected.

I've also attached a TZ.h file to pick your time zone. That's easier than setting the POSIX time zone value yourself.

#include "TZ.h"

void setup() {
  const int epochYear {1900};
  char timeString[100];

  Serial.begin(115200);
  delay(2000);

  setenv("TZ", TZ_America_New_York, 1);
  tzset();

  tm localTime1 = {
    0,   // seconds
    0,   // minutes
    15,  // hours
    17,  // day of month
    10,  // month is zero-based
    2024 - epochYear, // years since 1900
    0,
    0,
    0
  };

  time_t epochTime1 {mktime(&localTime1)};
  Serial.printf("Epoch Time1: %lld\n", epochTime1);
  tm *computedLocalTime1 {localtime(&epochTime1)};
  strftime(timeString, 100, "Converted Local Time1: %A, %B %d %Y %H:%M:%S %Z", computedLocalTime1);
  Serial.println(timeString);

  tm localTime2 = {
    0,   // seconds
    0,   // minutes
    16,  // hours
    17,  // day of month
    10,  // month is zero-based
    2024 - epochYear, // years since 1900
    0,
    0,
    0
  };

  Serial.println();
  time_t epochTime2 {mktime(&localTime2)};
  Serial.printf("Epoch Time2: %lld\n", epochTime2);
  tm *computedLocalTime2 {localtime(&epochTime2)};
  strftime(timeString, 100, "Converted Local Time2: %A, %B %d %Y %H:%M:%S %Z", computedLocalTime2);
  Serial.println(timeString);

  Serial.printf("\nEpoch Time2 - Epoch Time1 = %lld Seconds\n", epochTime2 - epochTime1);
}

void loop() {
}

TZ.h (22.2 KB)

Thank you!