from String to char[] for Timezone.h

I want to use JChristensen TimeZone lib and make the user fill it in over a webconnection.

This is the format the TimeZone.lib needs:

struct TimeChangeRule
{
    char abbrev[6];    // five chars max
    uint8_t week;      // First, Second, Third, Fourth, or Last week of the month
    uint8_t dow;       // day of week, 1=Sun, 2=Mon, ... 7=Sat
    uint8_t month;     // 1=Jan, 2=Feb, ... 12=Dec
    uint8_t hour;      // 0-23
    int offset;        // offset from UTC in minutes
};

What I get from user input is a String (currentLine), which I cut into parts. It's succesfull for uint8_t like this.

week_Summer=  (uint8_t)(currentLine.substring(place2+6,  place3).toInt());

// some more conversions and then:

TimeChangeRule UserSummer = {abbrev_Summer,week_Summer,dow_Summer,month_Summer,hour_Summer,offset_Summer};

It's not succesfull for the char array. This is what I've tried:

char abbrev_Summer[6];    // five chars max
currentLine.substring(place1+7,  place2).toCharArray(abbrev_Summer, 5);  
TimeChangeRule UserSummer = {abbrev_Summer,week_Summer,dow_Summer,month_Summer,hour_Summer,offset_Summer};


 // error is in last line:  array must be initialized with a brace-enclosed initializer

Then I tried:

char abbrev_Summer={0};    
currentLine.substring(place1+7,  place2).toCharArray(abbrev_Summer, 5);  
TimeChangeRule UserSummer = {abbrev_Summer,week_Summer,dow_Summer,month_Summer,hour_Summer,offset_Summer};

// error in second line is: invalid conversion from 'char' to 'char*' [-fpermissive]

Then

char abbrev_Summer={0};    
strncpy(abbrev_Summer,(currentLine.substring(place1+7,  place2)).c_str(),6);
TimeChangeRule UserSummer = {abbrev_Summer,week_Summer,dow_Summer,month_Summer,hour_Summer,offset_Summer};

// error in second line is: invalid conversion from 'char' to 'char*' [-fpermissive]

Then I tried a whole bunch of trial-and-error code from different parts of the web concluding that I don't exactly know what to do here....

EDIT: This is all on the ESP32.

make the user fill it in over a webconnection.

What I get from user input is a String (currentLine), which I cut into parts.

Please post the input received.

This is an example:
NB the week, dow etc need to be changed to numbers before it works.

GET /redirect?Summer=CEST&Winter=CET&abbrev=CEST&week=Last&dow=Sun&month=Mar&hour=2&offset=120&abbrev2=CET&week2=Last&dow2=Sun&month2=Oct&hour2=3&offset2=60 HTTP/1.1

Is that the exact line of received text you are trying to parse?

NB the week, dow etc need to be changed to numbers before it works.

I think it might be more simple to rely on the enums in the library and pull out the data to use like is done in the library examples

TimeChangeRule myCEST = {"CEST", Last, Sun, Mar, 2, 120};
  1. Yes, but the error already comes up when I 'verify' the code. That is without any user input.
  2. This could be the case, I'm also happy to work without enum.

My question is about the first item "CEST" in your example.

If I paste it in as literally "CEST" everything is fine. But i need it as a variable. Some kind of char. But how...?

since your struct contains an array of chars, you need to copy them in using strcpy() or pass each individual char into the declaration.

  TimeChangeRule UserSummer = { abbrev_Summer[0],abbrev_Summer[1],abbrev_Summer[2],abbrev_Summer[3],abbrev_Summer[4],abbrev_Summer[5],
        week_Summer, dow_Summer, month_Summer, hour_Summer, offset_Summer};

  TimeChangeRule User2 = { {""}, week_Summer, dow_Summer, month_Summer, hour_Summer, offset_Summer};
  strcpy( User2.abbrev, abbrev_Summer );

Thanks a lot. It seems to work! (I took your second code example). I will now look into 'structs' a bit more. :smiley:

Timezone.h contains a function .setRules() which allows for changing of the TimeZoneRules. I think you will need to use it.

#include <Timezone.h>    //https://github.com/JChristensen/Timezone
TimeChangeRule myCEST = {"", 0, 0, 0, 0, 0};
//TimeChangeRule myCEST = {"CEST", Last, Sun, Mar, 2, +120};   //Summer time = UTC +2 hours
TimeChangeRule myCET = {"CET", Last, Sun, Oct, 3, +60};     //Standard time = UTC +1 hours
Timezone myTZ(myCEST, myCET);
TimeChangeRule *tcr;//pointer to the time change rule, use to get TZ abbrev
time_t utc, local;

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

  setTime(0, 59, 30, 31, 3, 2019); //UTC(hr,min,sec,day,mnth,yr
  Serial.println(myCEST.abbrev);
  Serial.println(myCEST.week);
  Serial.println(myCEST.dow);
  Serial.println(myCEST.month);
  Serial.println(myCEST.hour);
  Serial.println(myCEST.offset);
  Serial.println();
  strcpy(myCEST.abbrev, "CEST");
  myCEST.week = Last;
  myCEST.dow = Sun;
  myCEST.month = Mar;
  myCEST.hour = 2;
  myCEST.offset = 120;
  myTZ.setRules(myCEST, myCET);
  Serial.println(myCEST.abbrev);
  Serial.println(myCEST.week);
  Serial.println(myCEST.dow);
  Serial.println(myCEST.month);
  Serial.println(myCEST.hour);
  Serial.println(myCEST.offset);
  Serial.println();
}

void loop(void)
{
  utc = now();
  local = myTZ.toLocal(utc, &tcr);
  printTime(local, tcr -> abbrev);
  delay(10000);
}
//Function to print time with time zone
void printTime(time_t t, char *tz)
{
  sPrintI00(hour(t));
  sPrintDigits(minute(t));
  sPrintDigits(second(t));
  Serial.print(' ');
  Serial.print(dayShortStr(weekday(t)));
  Serial.print(' ');
  sPrintI00(day(t));
  Serial.print(' ');
  Serial.print(monthShortStr(month(t)));
  Serial.print(' ');
  Serial.print(year(t));
  Serial.print(' ');
  Serial.print(tz);
  Serial.println();
}

//Print an integer in "00" format (with leading zero).
//Input value assumed to be between 0 and 99.
void sPrintI00(int val)
{
  if (val < 10) Serial.print('0');
  Serial.print(val, DEC);
  return;
}

//Print an integer in ":00" format (with leading zero).
//Input value assumed to be between 0 and 99.
void sPrintDigits(int val)
{
  Serial.print(':');
  if (val < 10) Serial.print('0');
  Serial.print(val, DEC);
}

This is a great example!

Unfortunately, I want to store my TimeRules in EEPROM and I'm working with the ESP32 board. I contacted JChristensen, but it will not work (as far as I can tell), see here

You and pert are way ahead of me :slight_smile:

I'm unclear what you mean by this. Can you please explain.

I also found that EEPROM does not work outside of the setup or loop function on the ESP32.

exit status 1
'EEPROM' does not name a type

cattledog:
You and pert are way ahead of me :slight_smile:

I'm unclear what you mean by this. Can you please explain.

I think there was a correction on it further down, a constructor uses the EEPROM but it is before the EEPROM.begin() in setup, at least that's what i understood from it, but as far as i know that only causes a runtime error, not a compile error so there must be something more to it.

Deva_Rishi:
I think there was a correction on it further down, a constructor uses the EEPROM but it is before the EEPROM.begin() in setup, at least that’s what i understood from it, but as far as i know that only causes a runtime error, not a compile error so there must be something more to it.

There is no such thing as EEPROM.begin(). All you need to do is

include <EEPROM.h>

and you can begin using read/write/put/update…

Have you looked at the ezTime library?
While not really related to your issues, it does have very good and easy to use timezone support and some storage routines.

--- bill

@ blh64 states

There is no such thing as EEPROM.begin().

This discussion is in the context of the ESP 32 and ESP 8266.

EEPROM.begin() with a parameter for the "size" of the memory managed as a pseudo EEPROM and EEPROM.commit() are required along with the other EEPROM.h functions.

@bperrybap That seems like a great alternative, tx!

@EEPROM discussion: On the ESP32 EEPROM.begin() needs to be called to start EEPROM emulation. If this is done outside a function it will cause an error.

Remzo:
If this is done outside a function it will cause an error.

but a constructor is a function isn't it ? so with ifdef within the library this could be achieved. Also as i said before, if you don't call it, then the EEPROM doesn't work, but no compiler errors as far as i know.
And of course also EEPROM.commit() or EEPROM.end() is required to actually save the data.

@EEPROM discussion: On the ESP32 EEPROM.begin() needs to be called to start EEPROM emulation. If this is done outside a function it will cause an error.

I'm not certain why using the emulated eeprom will be a problem if you don't modify the library.

EEPROM.begin() will be in setup. From @pert's commits it looks straight forward to use .put() and.get() to store the time change rules. Manage the storage and retreival of the rules (A character string and 4 integers) without the library. Transfer them into the library struct. You will not need to use the .readRules() and .writeRules().

What am I missing?

cattledog:
looks straight forward to use .put() and.get() to store the time change rules.
What am I missing?

ESP EEPROM does not have .put() & .get() (although they could be created) also if the object constructor already has calls to the EEPROM then putting the EEPROM.begin() within setup() is going to be to late.

For me this discussion is being a bit too technical to really comment on, but very interesting. I have moved on using

char a[]="CET-1CEST,M3.5.0,M10.5.0/3"// this will be user generated;

EEPROM.put(404,a);
EEPROM.commit();

and later

char a[50];
configTime(0, 0, "pool.ntp.org");
EEPROM.get(404,a);
setenv("TZ", a ,1);

which is simple, does not require another library and seems to work so far.

(on ESP32).

It then continues with not the best code like this (if people want to do the same).

int Hr,Min,Sec,Day,Month,Year;
char H[3], M[3],S[3],D[4],Mt[3],Y[4];

struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    LastUpdate = LastUpdate + 10000;  // try 10 sec later
  }
  else {LastUpdate = millis();
         Serial.println("Fresh new time arrived");}

  
  //See http://www.cplusplus.com/reference/ctime/strftime/



 
 strftime(H, 3, "%H", &timeinfo);
 Hr = atoi(H);

 strftime(M, 3, "%M", &timeinfo);
 Min = atoi(M);

 strftime(S, 3, "%S", &timeinfo);
 Sec = atoi(S);

 strftime(D, 3, "%d", &timeinfo);
 Day = atoi(D);

 strftime(Mt, 3, "%m", &timeinfo);
 Month = atoi(Mt);


 strftime(Y, 3, "%y", &timeinfo);
 Year = atoi(Y);
 
 Serial.println("####");
 Serial.println(Hr);
 Serial.println(Min); 
 Serial.println(Sec); 
 Serial.println(Day); 
 Serial.println(Month); 
 Serial.println(Year);

setTime(Hr,Min,Sec,Day,Month,Year);