Accessing the current date

Hi... complete noob here to Arduino and C++... I am having a heck of a time just trying to access the current date in my loop(). Any help is appreciated.

I've pieced together the following code that connects to an NTP and adjusts for my timezone... this (borrowed) code seems to be working just fine. Now, I'd like to access a function in my loop() that returns a String in the format "YYYY-MM-DD" of the current moment in time and I can't seem to get it right.

When the date rolls from one day to the next e.g. 2025-03-17 to 2025-03-18, my strings never change to the next day, whatever the "DD" was on the first run is all I get. Nothing I've tried works, so I've just omitted that part of the code. I have no idea what I'm doing wrong for such a simple task.

Board is an ESP32-WROOM-32D.

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <cstring>
#include <TimeLib.h>
#include <ctime>
/*#include <DS1307RTC.h>*/

WiFiMulti WiFiMulti;
struct tm timeinfo;

void setTimezone(String timezone){
  Serial.printf("  Setting Timezone to %s\n",timezone.c_str());
  setenv("TZ",timezone.c_str(),1);  //  Now adjust the TZ.  Clock settings are adjusted to show the new local time
  tzset();
}

void initTime(String timezone){
  Serial.println("Setting up time");
  configTime(0, 0, "0.us.pool.ntp.org");    // First connect to NTP server, with 0 TZ offset
  Serial.print(F("Waiting for NTP time sync: "));
  time_t nowSecs = time(nullptr);
  while (nowSecs < 8 * 3600 * 2) {
    delay(500);
    Serial.print(F("."));
    yield();
    nowSecs = time(nullptr);
  }

  Serial.println();
  if(!getLocalTime(&timeinfo)){
    Serial.println("  Failed to obtain time");
    return;
  }
  Serial.println("  Got the time from NTP");
  // Now we can set the real timezone
  setTimezone(timezone);
}

void printLocalTime(){
  
  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);

  Serial.println();
  Serial.println();
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP("SSID", "PASS");

  // wait for WiFi connection
  Serial.print("Waiting for WiFi to connect...");
  while ((WiFiMulti.run() != WL_CONNECTED)) {
    Serial.print(".");
  }
  Serial.println(" connected");

  //initTime("EST+5EDT,M3.2.0/2,M11.1.0/2");   // Set for EastCoast
  initTime("CST+6CDT,M3.2.0/2,M11.1.0/2");   // Set for Central

  printLocalTime();
}
void loop() {

  String dateString;   // ????
  dateString = formatDateForAPI(); // ????
  delay(1000*60*60*6); // check again in 6hrs
}

String formatDateForAPI(???) {
  ????
}

With the help of some AI, this is what I have now...I don't understand the function declaration. It seems to me it should be * if anything, not &. We'll see if this works as intended once the clock rolls tonight.

String formatDateForAPI2(const tm& currentTime)  // note pass by reference here
{
    /*
    int    tm_sec   seconds [0,61]
    int    tm_min   minutes [0,59]
    int    tm_hour  hour [0,23]
    int    tm_mday  day of month [1,31]
    int    tm_mon   month of year [0,11]
    int    tm_year  years since 1900
    int    tm_wday  day of week [0,6] (Sunday = 0)
    int    tm_yday  day of year [0,365]
    int    tm_isdst daylight savings flag
    */

    String year_str;
    String month_str;
    String day_str;

    year_str = String(currentTime.tm_year + 1900 );

    if ( String(currentTime.tm_mon + 1).length() < 2 )
    {
      month_str = "0" + String(currentTime.tm_mon + 1);
    }
    else {
      month_str = String(currentTime.tm_mon + 1);
    }

    if ( String(currentTime.tm_mday).length() < 2 )
    {
      day_str = "0" + String(currentTime.tm_mday);
    }
    else {
      day_str = String(currentTime.tm_mday);
    }
    
    Serial.println("-----Final string in the function2:");
    Serial.println(year_str + "-" + month_str + "-" + day_str );
    Serial.println("--------End Func2---------------");

    return (year_str + "-" + month_str + "-" + day_str);


}

void loop(){
String dateString;
  dateString = formatDateForAPI2(timeinfo);
}

You really should include that

  • it adds context
  • shows what you have tried
  • maybe you're thisclose and it's a minor error
  • makes answering the question easier
  • you may get more help, faster

How do you test what you have? Wait until almost midnight EDT, and if that doesn't work, wait an hour for CDT; then try again the next day? Oh: your second post answered that :slight_smile:

As a general note, when you're writing an .ino, you never need #include <Arduino.h> -- it is magically/silently added for you when compiling. It's only needed if you're writing your own "regular" .h or .cpp -- and you're referencing something Arduino-specific, like Serial or String

In C++, & means reference, as opposed to pass-by-value (i.e. making a copy). Behind the scenes, it is likely implemented with a pointer, but it is safer.

The AI-generated code is mediocre. If you already have a struct tm, try this

char date_ymd[] = "YYYY-MM-DD";
char time_hms[] = "HH:MM:SS";

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

void loop() {
  delay(1000);

  // get the current time as both epoch seconds and date/time
  time_t now;
  time(&now);
  tm datetime;
  localtime_r(&now, &datetime);

  // update date and time strings
  strftime(date_ymd, sizeof(date_ymd), "%F", &datetime);  // use specifiers
  strftime(time_hms, sizeof(time_hms), "%T", &datetime);  // added in C++11
  // and print them
  Serial.print(date_ymd);
  Serial.print(' ');
  Serial.println(time_hms);

  // manually adjust the clock to be around midnight
  if (datetime.tm_min == 0 && datetime.tm_sec >= 4) {
    now += 86400 - 9;
    timeval tv {.tv_sec = now, .tv_usec = 0};
    settimeofday(&tv, nullptr);  // no timezone
  }
}

Your first post had code to set the timezone and get the actual date and time. Which is fine, but not necessary to see if the date rolls over at midnight. It is actually just slightly counterproductive. Once the time is set -- even when initialized to 1970, the start of the Unix epoch -- it will increment automatically.

As pointed out by Kenb4, this is pretty kludgey. If you want to get a formatted string from a date and time - strftime is the way to go as seen below.

Thanks for this @kenb4 . Before your example, I didn't realize that a struct tm doesn't actually keep track of the time... whoops... Which was the biggest flaw in my code first posted. The only occurrence of my tm being set was in setup() that's why the date never changed from the first run.

Using your example - time, localtime_r, and strftime have got me what I needed. It seems to be working as intended now.