ESP8266 null sketch finds time. Why? How?

I have been playing around with various NTP solutions for a sketch that I've written for the ESP8266. In doing so I've discovered a "feature" that seems to be totally undocumented. I'm using the latest version of the Arduio IDE (1.8.10) and ESP8266 core (2.6.3). My ESP8266 is a D1 Mini connected to the PC serial port with a USB cable. If I create a basically null sketch and run it, the time() function seems to return a valid time after about 2 seconds. Why is this?

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

void loop() {
time_t current = time(nullptr);
Serial.print(ctime(&current));
delay(500);
}

Output on Serial port connected to the PC using the IDE Serial Monitor:

Thu Jan 1 08:00:02 1970
Thu Jan 1 08:00:02 1970
Tue Dec 24 08:01:17 2019
Tue Dec 24 08:01:17 2019
Tue Dec 24 08:01:18 2019
Tue Dec 24 08:01:18 2019
Tue Dec 24 08:01:19 2019
Tue Dec 24 08:01:19 2019
Tue Dec 24 08:01:20 2019
Tue Dec 24 08:01:20 2019
Tue Dec 24 08:01:21 2019
Tue Dec 24 08:01:21 2019

For the initial second (or so) I see a zero UNIX time plus 8 hours. Then I seen the current local time on my PC +13 hours. (My time zone is -5 hours). How is the time() getting set? According to all that I read the time should start at 0 and count up unless I set it specifically using NTP, an RTC, or a manual setTime() call. If I reset the ESP8266, it starts immediately at the localtime + 13 hours. If I powercycle it, it behaves as above.

because the Espressif SDK is linked to your sketch and cares about things. it remembers the last ssid and password and connects to wifi, then it retrieves time from configured NTP servers

Juraj:
because the Espressif SDK is linked to your sketch and cares about things. it remembers the last ssid and password and connects to wifi, then it retrieves time from configured NTP servers

Thanks for the info! Is there any way to control this behavior? Disable it, set the NTP servers or, preferably set the timezone offset? Where would I find the APIs and documentation to do so from the Arduino IDE?

Here's how I do it:

In setup():

 // implement NTP update of timekeeping (with automatic hourly updates)
  configTime(0, 0,0.pool.ntp.org);

  // info to convert UNIX time to local time (including automatic DST update)
  setenv("TZ", "EST+5EDT,M3.2.0/2:00:00,M11.1.0/2:00:00", 1);

You will have to do some research on 'TZ' to get the parameters for your location.

Don

Thanks. I did not realize the configTime actually used the native code in the ESP. I’ve tried playing with configTime, but what I observe that that at first I get the time in GMT+8 and then after a second or two it will change to the zone specified by the first argument to configTime (probably after it resyncs with the new timeserver specified by configTime). The problem with this is I don’t know how long to wait until it is a valid time in the specified zone. The example sketches I’ve found suggest that you test if time(nullptr) < 100000, but that does not work for the period of time where the time is valid but in GMT+8. I don’t want to put in an arbitrary, long delay because this is a battery powered datalogger, and I’d like to preserve battery power. Since I don’t really need localtime, it is not necessary to mess with the TZ environment in my case. I just want the time() function to return a valid time in the zone specified by configTime.

So, I think I've figured out a solution: Use configTime before WiFi.begin(). This causes the initial times to be in the desired timezone as soon as the time is available.

you can use WiFi.peristent() and WiFi.setAutoConnect()

https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/generic-class.html#persistent

Juraj:
you can use WiFi.peristent() and WiFi.setAutoConnect()

Generic Class — ESP8266 Arduino Core 3.0.1-3-g7ba596a5 documentation

Thanks!

After reading your response I realized that I left out a few details.

You need to include time.h (note the lowercase ‘t’).

You can register a callback which will execute whenever an NTP update has occurred. To do this you add this instruction after the other two in setup():

 settimeofday_cb(time_is_set);

You also need to include coredecls.h for the callback to work.

Here’s a complete program for you to try:

// Minimal code to implement NTP update of timekeeping on an ESP8266
// This code uses EspressIF functions introduced with the ESP32.  They work on 
//  recent versions of the ESP8266 core as well.
// Date and time information is output on the serial monitor.
// Time is derived from an NTP time server

// Required libraries
#include <ESP8266WiFi.h>
#include <time.h>
#include <coredecls.h>                  //   required for settimeofday_cb()

const char* ssid = "...";               // put your info here
const char* pass = "...";

// Timing parameters
int data_update_minutes = 1;            // measurement interval in minutes
time_t present_timestamp;
time_t previous_timestamp = 0;

// ....................................................................................
 
void setup()
{
  // make sure the ESP8266 WiFi functions are enabled
  WiFi.mode(WIFI_STA);                  // use only the WiFi 'station' mode
  
  Serial.begin(115200);
  
  Serial.println("\n Display date and time in several formats");

  // minimal code to log onto a local WiFI network
  WiFi.begin(ssid, pass);               // send credentials
  while (WiFi.status() != WL_CONNECTED) // wait for connection
  {
    delay(500);
    Serial.print(".");
  } 
  
  // implement NTP update of timekeeping (with automatic hourly updates)
  configTime(0, 0, "0.pool.ntp.org");

  // info to convert UNIX time to local time (including automatic DST update)
  setenv("TZ", "EST+5EDT,M3.2.0/2:00:00,M11.1.0/2:00:00", 1); 
  
   // register a callback (execute whenever an NTP update has occurred)
  settimeofday_cb(time_is_set);
 
  Serial.println("\nSetup done");  
 
}

// ....................................................................................
 
void loop()
{
  present_timestamp = time(nullptr);

  if (present_timestamp >= previous_timestamp + (60 * data_update_minutes)) 
  {
    previous_timestamp = present_timestamp;
    Serial.println();  
    displayDateAndTime(); 
  }    
    
}

// ....................................................................................
 
void time_is_set (void)
// callback routine - arrive here whenever a successful NTP update has occurred
{
  struct tm *tmp ;                      // NOTE: structure tm is defined in time.h
 
  char UPDATE_TIME[50];                 // buffer for use by strftime()
    
  // display time when NTP update occurred
  time_t tnow = time(nullptr);          // get UNIX timestamp 
  tmp = localtime(&tnow);               // convert to local time and break down
  strftime(UPDATE_TIME, sizeof(UPDATE_TIME), "%T", tmp);  // extract just the 'time' portion
    
  Serial.print("\n-------- NTP update at ");
  Serial.print(UPDATE_TIME);
  Serial.println(" --------");
}


// ....................................................................................
void displayDateAndTime()
{
  Serial.print(present_timestamp);      // display system time as UNIX timestamp
  
  // use ctime() to convert the system (UNIX) time to a local date and time in readable form
  // NOTE: 'ctime' produces a output in a specific format that looks 
  //        like --> Fri Mar 22 12:11:51 2019  -there is also a newline (\n) appended
  Serial.print("  ");
  Serial.print(ctime(&present_timestamp));  // convert timestamp and display
  
  struct tm *tmp ;                          // NOTE: structure tm is defined in time.h
  char FORMATTED_TIME[50];                  //   filled by strftime()
    
  // convert the system (UNIX) time to a local date and time in a configurable format

  tmp = localtime(&present_timestamp);      // break down the timestamp     
      
  // use strftime() to display the date and time in some other formats
  // https://www.geeksforgeeks.org/strftime-function-in-c/ 

  // the following gives output in this form -->  03/22/19 - 12:11 pm EDT
    //   where %x --> writes localized date representation
    //         %I --> writes hour as a decimal number, 12 hour clock (range [01,12])

    //         %M --> writes minute as a decimal number (range [00,59]) 
    //         %P --> writes localized a.m. or p.m. (locale dependent)
    //         %Z --> time zone abbreviation name
  strftime(FORMATTED_TIME, sizeof(FORMATTED_TIME), "%x - %I:%M%P %Z", tmp);  
  Serial.println(FORMATTED_TIME);
 
  // the following gives output in this form -->  2019-03-22 12:11:51
    //         %F --> equivalent to "%Y-%m-%d" (the ISO 8601 date format)     
    //         %T --> equivalent to "%H:%M:%S" (the ISO 8601 time format) 
  strftime(FORMATTED_TIME, sizeof(FORMATTED_TIME), "%F %T", tmp);
  Serial.println(FORMATTED_TIME);
    
  Serial.println();
}

Don

If I have the ESP-01 modules and use the AT commands, this feature is not available?

.

ieee488:
If I have the ESP-01 modules and use the AT commands, this feature is not available?

.

the NTP time? AT+CIPSNTPCFG and AT+CIPSNTPTIME. see the reference

Juraj:
the NTP time? AT+CIPSNTPCFG and AT+CIPSNTPTIME. see the reference

thank you!
saving this information

.

Just getting starting working with Arduino nodeMCU. Loved the example code to get time from NTP server. Thanks for posting it.