Extracting the time

Please be kind as i am guessing this is probably easy for most of you.
I'm an old Basic/Cobol programmer from the 70's and struggling learning C++
My problem... I am trying to extract the date/time from a function in the Samples/esp32/Time/time sample sketch.
I know this should be easy but everything I try doesn't give the results I am expecting.

Line 21...... Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
displays the returned date and time as expected.

but lines 27,28,29
day == String((&timeinfo, "%A"));
month == String((&timeinfo, "%B"));
year == String((&timeinfo, "%Y"));

dosnt return anything, the variables day,month and year still hold the initial values.
How do I get the values returned into the strings.
I have tried all sorts but just end up with %A or nothing.


( I need to be able to tag the date and time to the end of a string I push out to call me bot in another sketch

String url = "https://api.callmebot.com/whatsapp.php?phone=" + telephoneNumber + "&apikey=" + whatsappAPIKey + "&text=" + urlEncode("** Laser Stopped ! ** ") +count + date + time;


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

const char *ssid = "Paul2";
const char *password = "abcdeabcde";

const char *ntpServer1 = "pool.ntp.org";
const char *ntpServer2 = "time.nist.gov";
const long gmtOffset_sec = 0;
const int daylightOffset_sec = 0;
String day = "Tuesday";
String month = "December";
String year = "1965";
const char *time_zone = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone rule for Europe/Rome including daylight adjustment rules (optional)

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");
    Serial.println(&timeinfo, "%A");
    Serial.print( "Day before ");
    Serial.println(day);
  day == String((&timeinfo, "%A"));
  month == String((&timeinfo, "%B"));
  year == String((&timeinfo, "%Y"));
    Serial.print( "Day ");
    Serial.print(day);
    Serial.print( " Month ");
    Serial.print(month);
    Serial.print( " year ");
    Serial.println(year);
}


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

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

  // First step is to configure WiFi STA and connect in order to get the current time and date.
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);

  /**
   * NTP server address could be acquired via DHCP,
   *
   * NOTE: This call should be made BEFORE esp32 acquires 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 acquired NTP server address
   */
  esp_sntp_servermode_dhcp(1);  // (optional)

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" CONNECTED");

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

  /**
   * 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 automagically.
   */
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);

  /**
   * A more convenient approach to handle TimeZones with daylightOffset
   * would be to specify a environment 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);
}

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

As your topic does not relate directly to the installation or operation of the IDE it has been moved to the Programming Questions category of the forum

Double-equal is for comparison: equal-to. The program will evaluate both sides, which could have side-effects, and then either

  • check to see if they are equal, which may involve calling other code with operator overloading; or
  • the compiler sees that the result is ignored anyway, and decides to generate no code for it

To assign, use a single equals sign.

1 Like

Why not lust read the required elements of the timeinfo like this

    day = String(timeinfo.tm_mday);
1 Like

Hi UKHeliBob,

Thanks for that !
I did scratch my head for a while how you had come up with (timeinfo.tm_mday)
After much searching I see its in the time.h.
I guess I am going to have to do a lot more programming and math to work out how to extract the Month and other bits from the given verifiable.

     tm_sec; /**< seconds after the minute - [ 0 to 59 ] */
     tm_min; /**< minutes after the hour - [ 0 to 59 ] */
     tm_hour; /**< hours since midnight - [ 0 to 23 ] */
     tm_mday; /**< day of the month - [ 1 to 31 ] */
     tm_wday; /**< days since Sunday - [ 0 to 6 ] */
     tm_mon; /**< months since January - [ 0 to 11 ] */
     tm_year; /**< years since 1900 */
     tm_yday; /**< days since January 1 - [ 0 to 365 ] */
     tm_isdst; /**< Daylight Saving Time flag */

Thank you
Paul

Hi kenb4,

I have tried this with a single = not sure why I put doubles in !

But it still doesnt work with 1, it returns the as "%Y" and not the value of %Y

Thank you
paul

As you have discovered, timeinfo is an instance of the tm struct

    struct tm timeinfo;

Using IDE 2 you can right click on the name of the struct and see how it is defined

A comma is almost exclusively used to separate multiple items where expected or allowed; e.g. the elements of an array inside braces { }, or the arguments to a function inside parentheses ( ). But parentheses are also for grouping. So this

String(&timeinfo, "%Y")

calls the String constructor with two arguments

  • the address of (a pointer to) a struct tm
  • a const char[3]

There is no matching signature, so you get an error. Did you get that error?

Adding the extra parentheses, you now have a single expression. Multiple expressions separated by commas result in a single value: the last expression. So

(&timeinfo, "%Y")
(123, "%Y")
(true, false, 123, 456, "%Y")

are all just "%Y"; and String("%Y") is just that as a String instead of a const char[]. This "last expression" behavior isn't necessarily useful, but a compound expression has to have a value of some kind; so that's the rule. A more useful reason to have more than one expression where it is not necessarily expected is for example

  for (int x = 7, y = 3; x && y; --x, --y) {
    Serial.println(x * y);
  }

Because you're on ESP32

works because

  • Serial is an instance of HardwareSerial
  • HardwareSerial is a subclass of Stream
  • Stream is a subclass of Print
  • Print has about a dozen signatures for both print and println, one of which is
    size_t println(struct tm * timeinfo, const char * format = NULL);
    

You can see this for yourself in the IDE (once your sketch has been indexed) by right-clicking on something like Serial and choosing Go to Definition. And that method

size_t Print::println(struct tm * timeinfo, const char * format)
{
    size_t n = print(timeinfo, format);
    n += println();
    return n;
}

just calls print and adds the line break, so the real action is there

size_t Print::print(struct tm * timeinfo, const char * format)
{
    const char * f = format;
    if(!f){
        f = "%c";
    }
    char buf[64];
    size_t written = strftime(buf, 64, f, timeinfo);
    if(written == 0){
        return written;
    }
    return print(buf);
}

Note above that the format argument has a default value of NULL, so this will also be called with just one struct tm * argument to print or println. If the format string is null, explicitly or by default, use "%c". Then allocate a "large enough" buffer -- this limit is not mentioned anywhere -- and call the library function that does all the real work with the different format options, strftime.

You can call strftime yourself, and if you want the results in a String, you can just do String(buf) after.

A general question about printing the values of the timeinfo struct

Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");

Where are the names used in the format defined ?

From strftime() See Print.cpp in the ESP32 core:

size_t Print::print(struct tm * timeinfo, const char * format)
{
    const char * f = format;
    if(!f){
        f = "%c";
    }
    char buf[64];
    size_t written = strftime(buf, 64, f, timeinfo);
    if(written == 0){
        return written;
    }
    return print(buf);
}

You mean the % specifiers? I'm guessing just like with printf, hard-coded somewhere, and hopefully well-documented. My link to strftime above is decent.

Yes, those

That is what I was hoping too but I have yet to find it

I have found some information about the strftime() function format specifiers

More detailed; harder to read

https://en.cppreference.com/w/c/chrono/strftime

Thank you Bob,

with you guys help I've got my sketch working ..
A lot of what I have read has been over my head lol

This is what I have gone for ..

#include <Stream.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <UrlEncode.h>
#include <WiFi.h>
#include "time.h"
#include "esp_sntp.h"

const char *ntpServer1 = "pool.ntp.org";
const char *ntpServer2 = "time.nist.gov";
const long gmtOffset_sec = 0;     // was 3600
const int daylightOffset_sec = 3600;  //3600
const char *time_zone = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone rule for Europe/Rome including daylight adjustment rules (optional)
                                                        // Not sure how to change this to uk so changed gmtoffsett_sec to = 0 
char ssid[] = "Paul2";           // network SSID (name)
char password[] = "abcdeabcde";  // network password
const int ard = 5;
int ardtest=1;
int count=0;
String message ="** Garage Sensor Startup ** ";
String telephoneNumber = "4479538";
String whatsappAPIKey = "1256";
String hours ="xx";
String mins = "xx";
String tim =" xx:xx";
String day = "xxx";
String dday = "xxx";
String month = "xxxxx";
String year = "xxxx";
 
 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");
    year = String(timeinfo.tm_year +1900);
    hours = String(timeinfo.tm_hour);
    mins = String(timeinfo.tm_min);
    dday = String(timeinfo.tm_mday);
    day = String(timeinfo.tm_wday);
      if (day =="0") { day="Sunday ";}
      if (day =="1") { day="Monday ";}
      if (day =="2") { day="Tuesday ";}
      if (day =="3") { day="Wednesday ";}
      if (day =="4") { day="Thursday ";}
      if (day =="5") { day="Friday ";}
      if (day =="6") { day="Saturday ";}
  
    month = String(timeinfo.tm_mon);
      if (month =="0") { month=" January ";}
      if (month =="1") { month=" February ";}
      if (month =="2") { month=" March ";}
      if (month =="3") { month=" April ";}
      if (month =="4") { month=" May ";}
      if (month =="5") { month=" June ";}
      if (month =="6") { month=" july ";}
      if (month =="7") { month=" August ";}
      if (month =="8") { month=" September ";}
      if (month =="9") { month=" October ";}
      if (month =="10") { month=" November ";} 
      if (month =="11") { month=" December ";}
  
    tim = " " + day + dday + month + year + " " + hours + ":" + mins;
}

// Callback function (gets called when time adjusts via NTP)
void timeavailable(struct timeval *t) {
  Serial.println("Got time adjustment from NTP!");
  printLocalTime();
}
 
 namespace whatsApp {
  void sendWhatsAppMsg() {
  String url = "https://api.callmebot.com/whatsapp.php?phone=" + telephoneNumber + "&apikey=" + whatsappAPIKey + "&text=" + urlEncode("** Garage Entry ! **  ") +count + urlEncode(tim);
   HTTPClient http;
  http.begin(url);
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  int httpResponseCode = http.POST(url);
  if (httpResponseCode == 200) {
   Serial.print("WhatsApp message1 sent successfully");
  } else {
        Serial.print("Error sending WhatsApp message1. HTTP response code:");
  Serial.println("in send message loop");
  }
      //http.end();
}
} // End namespace WhatsApp

void setup() {
  pinMode(ard, INPUT_PULLUP);
  Serial.begin(9600);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
  // Attempt to connect to Wifi network:
  Serial.print("Connecting Wifi: ");
  Serial.println(ssid);
  
  esp_sntp_servermode_dhcp(1);  // (optional)

  WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      delay(500);
   }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  IPAddress ip = WiFi.localIP();
  Serial.println(ip);
  sntp_set_time_sync_notification_cb(timeavailable);
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);

  // Power up message before loop
  whatsApp::sendWhatsAppMsg();
   message =" Garage Detect ";
   count=1;
}
void loop() {
   
  delay(5000);
  printLocalTime();  // it will take some time to sync time :)
  Serial.println(tim);
 
  ardtest = digitalRead(ard);
   if (ardtest==1){

     whatsApp::sendWhatsAppMsg();
     //Serial.print(ardtest);
     Serial.print(" Movement Detected ");
     Serial.println(count);  
     count=count+1;
       delay(8000);
       delay(8000);// remove for cat flap detector keep for garage 
       delay(8000);// as above
       delay(8000);// as above 

    }   else {
    Serial.print(ardtest);
    Serial.println("  Else loop no movement detected ");
    delay(300);
        }
   }

I am glad that you got it working

A suggested improvement

    day = String(timeinfo.tm_wday);
      if (day =="0") { day="Sunday ";}
      if (day =="1") { day="Monday ";}
      if (day =="2") { day="Tuesday ";}
      if (day =="3") { day="Wednesday ";}
      if (day =="4") { day="Thursday ";}
      if (day =="5") { day="Friday ";}
      if (day =="6") { day="Saturday ";}

Leave day as an integer instead of converting to s String, put the day names in an array and use day as the index to get the name. Do the same for month names

A post was split to a new topic: HTTPClient.h error