SimpleTime not adjusting timezone parameter ? [SOLVED]

Using the SimpleTime sketch for ESP32 I went in an adjusted the
parameter that defines which timezone is referenced.

Despite trying several different values the time always comes back
off by 7 hours. It says it is 7 hours ahead of my Central Time Zone time. Is there something I am not understanding?

#include <WiFi.h>
#include "time.h"
#include "sntp.h"

const char* ssid       = "Homestead-LAN_EXT";
const char* password   = "xxxxxxxxx";

const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

const char* time_zone = "CST6CDT,M3.2.0,M11.1.0";  // Central Time Zone value used from:  https://sites.google.com/a/usapiens.com/opnode/time-zones

void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("No time available (yet)");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

// Callback function (get's called when time adjusts via NTP)
void timeavailable(struct timeval *t)
{
  Serial.println("Got time adjustment from NTP!");
  printLocalTime();
}

void setup()
{
  Serial.begin(115200);

  // set notification call-back function
  sntp_set_time_sync_notification_cb( timeavailable );

  /**
   * NTP server address could be aquired via DHCP,
   *
   * NOTE: This call should be made BEFORE esp32 aquires IP address via DHCP,
   * otherwise SNTP option 42 would be rejected by default.
   * NOTE: configTime() function call if made AFTER DHCP-client run
   * will OVERRIDE aquired NTP server address
   */
  sntp_servermode_dhcp(1);    // (optional)

  /**
   * This will set configured ntp servers and constant TimeZone/daylightOffset
   * should be OK if your time zone does not need to adjust daylightOffset twice a year,
   * in such a case time adjustment won't be handled automagicaly.
   */
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);

  /**
   * A more convenient approach to handle TimeZones with daylightOffset 
   * would be to specify a environmnet variable with TimeZone definition including daylight adjustmnet rules.
   * A list of rules for your zone could be obtained from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h
   */
  //configTzTime(time_zone, ntpServer1, ntpServer2);

  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  Serial.println(" CONNECTED");

}

void loop()
{
  delay(5000);
  printLocalTime();     // it will take some time to sync time :)
}

would that work ?

#include <WiFi.h>
#include "time.h"

const char * ssid = "Homestead-LAN_EXT";
const char * wifipw = "xxx";

void setTimezone(const char * timezone) {
  Serial.print("Setting Timezone to ");
  Serial.println(timezone);
  setenv("TZ", timezone, 1);
  tzset();
}

void initTime(const char * timezone) {
  struct tm timeinfo;

  Serial.println("Getting time from NTP server");
  configTime(0, 0, "pool.ntp.org");    // First connect to NTP server, use 0 TZ offset
  if (!getLocalTime(&timeinfo)) {
    Serial.println("  Failed to obtain time");
    return;
  }
  Serial.println("OK, Got the time from NTP");
  setTimezone(timezone); // then set your timezone
}

void printLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S zone %Z %z ");
}

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);

  WiFi.begin(ssid, wifipw);
  Serial.println("Connecting Wifi");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println("\nWifi OK");

  initTime("CST6CDT,M3.2.0,M11.1.0"); // https://sites.google.com/a/usapiens.com/opnode/time-zones
}

void loop() {
  printLocalTime();
  delay(5000);
}

Thank you very much.... works 1st time every time that way!!!
:+1: :+1: :+1:

1 Like

FWIW, you can configure the time zone and NTP servers all in one shot with the configTzTime() function.

Here's some code I wrote for a similar forum thread. It contains a lot of diagnostic printing for illustration purposes. You can see all of it by setting the Core Debug level to "Verbose". Set it to "Debug" or "Info" to see progressively less.

#include "Arduino.h"
#include <WiFi.h>
#include "esp_sntp.h"

void handleWifiEvent(arduino_event_id_t event, arduino_event_info_t info);
void periodicProcessing(void *pvParameters);

void setup() {
  const char ssid[] = "XXXX";
  const char password[] = "YYYYY";
  const char ntpServer1[] = "time.nist.gov";
  const char ntpServer2[] = "pool.ntp.org";
  const uint32_t syncInterval = 3600UL * 1000;
  const char localTimeZone[] = "EST5EDT";
  tm *timeinfo;
  timeval tv;
  time_t now;
  uint8_t retryTest = 0;
  uint8_t attemptsBeforeReset = 5;

  Serial.begin(115200);
  vTaskDelay(3000);

  log_v("Station MAC Address: %s", WiFi.macAddress().c_str());
  WiFi.onEvent(handleWifiEvent);

  log_i("Connecting to %s", ssid);
  while (WiFi.status() != WL_CONNECTED) {
    if (retryTest == 0) {
      WiFi.disconnect();
      WiFi.begin(ssid, password);
      retryTest = 5;
    } else {
      log_v(".");
      retryTest--;
      vTaskDelay(1000);
    }
  }

  //init and get the time
  sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
  sntp_set_sync_interval(syncInterval);
  configTzTime(localTimeZone, ntpServer1, ntpServer2);

  log_i("Syncing Time to NTP");
  retryTest = 0;
  while (true) {                 // Wait for NTP sync to take effect
    if (retryTest == 0) {
      if ((--attemptsBeforeReset) == 0) {
        ESP.restart();
      }
      log_i("Waiting for Time Sync");
      sntp_sync_time(&tv);
      retryTest = 10;
    } else {
      log_v(".");
      retryTest--;
    }
    vTaskDelay(2000);
    now = time(nullptr);
    timeinfo = localtime(&now);
    if (timeinfo->tm_year >= (2022 - 1900)) {
      break;
    }
  }

  sntp_sync_status_t syncStatus = sntp_get_sync_status();
  switch (syncStatus) {
  case SNTP_SYNC_STATUS_RESET:
    log_i("SNTP_SYNC_STATUS_RESET");
    break;

  case SNTP_SYNC_STATUS_COMPLETED:
    log_i("SNTP_SYNC_STATUS_COMPLETED");
    break;

  case SNTP_SYNC_STATUS_IN_PROGRESS:
    log_i("SNTP_SYNC_STATUS_IN_PROGRESS");
    break;

  default:
    log_e("Unknown Sync Status");
    break;
  }

  sntp_sync_mode_t mode = sntp_get_sync_mode();
  switch (mode) {
  case SNTP_SYNC_MODE_IMMED:
    log_i("SNTP_SYNC_MODE_IMMED");
    break;

  case SNTP_SYNC_MODE_SMOOTH:
    log_i("SNTP_SYNC_MODE_SMOOTH");
    break;

  default:
    log_e("Unknown Sync Mode");
    break;
  }

  uint32_t interval = sntp_get_sync_interval();
  log_i("NTP Sync Interval = %d", interval);

  timeinfo = gmtime(&now);
  log_i("UTC Time Info:");
  log_i("Year = %d, Mon = %d, Day = %d", timeinfo->tm_year, timeinfo->tm_mon, timeinfo->tm_mday);
  log_i("Hour = %d, Min = %d, Sec = %d", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
  log_i("DoY = %d, DoW = %d, IsDST = %d", timeinfo->tm_yday, timeinfo->tm_wday, timeinfo->tm_isdst);
  log_i("TZ = %s", getenv("TZ"));

  vTaskDelay(1000);
  BaseType_t returnCode = xTaskCreatePinnedToCore(periodicProcessing, "Periodic Processing", 3000, NULL, 8, NULL, CONFIG_ARDUINO_RUNNING_CORE);
  if (returnCode != pdPASS) {
    log_e("Failed to create handler task");
    vTaskDelay(2000);
    ESP.restart();
  }
}

void periodicProcessing(void *pvParameters) {
  const TickType_t delayTime = 5000;
  TickType_t wakeTime = xTaskGetTickCount();
  tm *timeinfo;
  time_t now;
  char timeString[100];

  for (;;) {
    now = time(nullptr);
    timeinfo = localtime(&now);
    strftime(timeString, 100, "%A, %B %d %Y %H:%M:%S", timeinfo);
    log_i("Local Time: %s", timeString);
    xTaskDelayUntil(&wakeTime, delayTime);
  }
}

void IRAM_ATTR handleWifiEvent(arduino_event_id_t event_id, arduino_event_info_t info) {

  switch (event_id) {
  case ARDUINO_EVENT_WIFI_READY:
    log_v("WiFi Ready");
    break;

  case ARDUINO_EVENT_WIFI_STA_START:
    log_v("WiFi Start");
    break;

  case ARDUINO_EVENT_WIFI_STA_STOP:
    log_v("WiFi Stop");
    break;

  case ARDUINO_EVENT_WIFI_STA_CONNECTED:
    log_d("Connected to SSID: %s", reinterpret_cast<char*>(info.wifi_sta_connected.ssid));
    break;

  case ARDUINO_EVENT_WIFI_STA_GOT_IP: {
    uint8_t bytes[4];
    uint32_t ipAddress = info.got_ip.ip_info.ip.addr;
    for (uint8_t i = 0; i < 4; i++) {
      bytes[i] = ipAddress & 0xFF;
      ipAddress >>= 8;
    }
    log_d("Got IP Address: %d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);
    break;
  }

  case ARDUINO_EVENT_WIFI_STA_LOST_IP:
    log_v("IP Address Lost");
    break;

  case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
    log_v("Disconnected From SSID: %s, Reason: %d", reinterpret_cast<char*>(info.wifi_sta_disconnected.ssid),
        info.wifi_sta_disconnected.reason);
    break;

  default:
    log_v("WiFi Event: %d", static_cast<uint8_t>(event_id));
    break;
  }
}

void loop() {
}

Since you are up on this date:time retrieval process I need to ask a question.
Regarding the printLocalTime() function:

void printLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S zone %Z %z ");

If I want to retrieve just the hour & minute elements of that tm structure to compare against
an integer value does the processor go out and request a time update off the web each time or use the values stored in the processor as system time? I want to avoid repeatedly accessing the web just to do a time value comparison. And do I need to add the "struct tm timeinfo;" line of code to the void loop for it to work properly?
thanks....

int hour = timeinfo.tm_hour;
int min = timeinfo.tm_min;

I don't totally understand your question. These lines first pull the Unix Epcho time from the ESP32's internal RTC and then use it to calculate the local time using the TZ environmental variable. This is also done internally.

		now = time(nullptr);
		timeinfo = localtime(&now);

The only time the internet is involved is when the internal RTC is synched to the NTP server. The rate at which this happens is set by the sntp_set_sync_interval() function.

To get individual time elements, check out the fields of the 'tm' structure in 'time.h':

struct tm
{
  int	tm_sec;
  int	tm_min;
  int	tm_hour;
  int	tm_mday;
  int	tm_mon;
  int	tm_year;
  int	tm_wday;
  int	tm_yday;
  int	tm_isdst;
#ifdef __TM_GMTOFF
  long	__TM_GMTOFF;
#endif
#ifdef __TM_ZONE
  const char *__TM_ZONE;
#endif
};

thank you.... even without understanding me fully you managed to answer my
convoluted questioning...lol :+1: :+1:

Just to add to your understanding, the ESP32 Arduino core print() function added support for a format string when you pass a struct tm to be printed out.

The print code is

As you can see, it’s just a wrapper for the standard function strftime()

Appreciate the addional input but admittedly I do not fully understand strftime and will
need to read it up on it at some point. The more I learn the more I realize how little I really know about C. :crazy_face:

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