Get Local Time from Internet

1. Motivation

While skimming code examples, I kept seeing complicated scenarios of how to fetch the local time from an NTP server. I want to show here that basically two function calls are enough.

2. Timezone and Dayligt Saving Time

Because all NTP servers provide Coordinated Universal Time (UTC) or as it used to be called Greenwich Mean Time (GMT), this time must be corrected for time zone and daylight saving to get Local Standard Time (LST)  LST + offset = UTC . This is done with the help of a string containing the name of the time zone, the offset to UTC, and the rules for the change from standard time to daylight saving time. Some examples:

  // MEZ  : Mitteleuropäische Zeit
  // MESZ : Mitteleuropäische Sommerzeit  
  // EST  : Eastern Standart Time
  // EDT  : Eastern Daylight Time
  // Two equivalent definitions
  // WGT  : Western Greenland time
  // WGST : Western Greenland Summer Time

The meaning of the individual fields can be found in this description of the TZ database.

3. Functions

After we have established a WiFi connection, we need a single function call to start a sntp-client, initialize it with the time zone and set the real time clock of the ESP32.

  const char TIME_TONE[] = "MEZ-1MESZ-2,M3.5.0/02:00:00,M10.5.0/03:00:00";
  const char NTP_SERVER_POOL = "";

That's it!

After calling configTzTime() we could close the WiFi connection and the ESP32 RTC would keep the time. But because the internal reference frequency of the ESP32 can be inaccurate and temperature dependent, we keep the WiFi connection and leave the periodic synchronisation with the NTP server also to the internals of configTzTime().

In our program we can now query the exact time at any time with another function call:

  tm   rtcTime;

This function call fills the struct tm with the complete time information. The structure is defined as follows:

  struct tm
    int	tm_sec;   // 0..59
    int	tm_min;   // 0..59
    int	tm_hour;  // 0..23 hours since midnight
    int	tm_mday;  // 1..31 day of month
    int	tm_mon;   // 0..11 January=0 .. December=11
    int	tm_year;  // years since 1900
    int	tm_wday;  // 0..6  weekday, Sunday=0 .. Saturday=6
    int	tm_yday;  // 0..365 day in the year, January 1 = 0
    int	tm_isdst; //  0: Standard time, 
                  // >0: Daylight saving time, 
                  // <0: information not available 

Because accessing the many individual fields of the structure is somewhat cumbersome, there is another useful function that allows the formatted output of the time information into a string. The format string can contain various format specifiers. Some examples shall clarify this:

  char buf[40];

  strftime(buf, sizeof(buf), "%T", &rtcTime);                // 16:33:20
  strftime(buf, sizeof(buf), "%F", &rtcTime);                // 2019-01-15
  strftime(buf, sizeof(buf), "%c", &rtcTime);                // Tue Jan 15 16:33:20 2019
  strftime(buf, sizeof(buf), "%B %d %Y %T (%A)", &rtcTime);  // January 15 2019 16:33:20 (Tuesday)  
  strftime(buf, sizeof(buf), "%F %T %W/%w %Z %z", &rtcTime); // 2019-01-15 16:33:20 02/2 MEZ +0100	

A complete description of the formatting options is available in the C++ reference.

4. Program Code

In the example program I show besides the display of date and time in different formats, also how to synchronize the output to full 5, 10, 15, ... seconds and display the time information periodically.

Interested? Please download the entire program code. The zip-file contains the complete PlatformIO project.

My programming environment is not the native Arduino™ IDE but PlatformIO™ on top of Microsoft's Visual Studio Code™. This combination offers many advantages and allows a much better structuring of the code into several modules especially when we adopt The Object Oriented way.