2

I must be missing something I'm attempting to get the timezone global variable value which is supposed to be set after TZset is called.

void setTimezone(String timezonestr){
   Serial.printf("  Setting Timezone to %s\n",timezonestr.c_str());
   setenv("TZ",timezonestr.c_str(),1);  
  tzset();
    Serial.print(timezone);

}

The error that I'm receiving is that

sketch.ino: In function 'void setup()': sketch.ino:75:24: error: expected primary-expression before ')' token
Serial.print(timezone);

Reading the docs I thought this was a global variable that should be set after tzset is called.

Feel free to take a look at the Messy trial and error code...

https://wokwi.com/projects/359035878235429889

After some searching I was able to find:

After calling the tzset() function in a C program, the timezone information is stored in a number of global variables that can be accessed by your program. These global variables are:

extern long timezone: This variable stores the difference in seconds between Coordinated Universal Time (UTC) and the local standard time. extern int daylight: This variable is non-zero if the local timezone is currently in daylight saving time, and zero otherwise. extern char *tzname[]: This is an array of two character strings that store the names of the local standard time and daylight saving time, respectively. To access these global variables in your program, you can simply refer to them by name, just like any other global variable. For example:

Thanks

                    ^
Naruto_333
  • 33
  • 3
BostonMacOSX
  • 1,369
  • 2
  • 17
  • 38
  • You did `#include ` right? – Nate Eldredge Mar 15 '23 at 15:14
  • `setenv` is definitely a no-op in the embedded bare metal environment. What documentation do you read? – 0___________ Mar 15 '23 at 15:20
  • In any case, the error message you show has nothing to do with the presence or absence of a `timezone` variable. (And the error message you show doesn't appear to have anything to do with the code you've shown.) – Steve Summit Mar 15 '23 at 15:22
  • @NateEldredge Affirmative – BostonMacOSX Mar 15 '23 at 15:33
  • @SteveSummit sorry had a couple different veriosn of the code lying around...I updated the main comment. – BostonMacOSX Mar 15 '23 at 15:45
  • @0___________ any basic Arduino Esp32 timezone library tutorial..on how to adjust UTC to your local timezone with time.h – BostonMacOSX Mar 15 '23 at 15:50
  • @BostonMacOSX want to share a link to one you followed? – romkey Mar 15 '23 at 16:04
  • @romkey added a tutorial text about tzset() – BostonMacOSX Mar 15 '23 at 16:23
  • It's true that setting the environment variable `TZ` is (sadly) the only way to change the timezone in C. It's true that calling `setenv` (or `putenv`), and then `tzset`, is the typical way to do this, and it works fine (despite its ghastliness) in high-level code under hosted environments. I agree with 0___________ that manipulating the environment may be dicey in an embedded environment. And although `TZ` and `tzset` are still current, that text you found about the `timezone`, `daylight`, and `tzname` variables is pretty much obsolete. – Steve Summit Mar 15 '23 at 16:32
  • It's not recommended to use those variables any more, and while they may still exist in some traditional Unix environments, I'd be surprised if they exist anywhere else. – Steve Summit Mar 15 '23 at 16:33
  • @SteveSummit so the issue I'm trying to solve is to get my TZ Offset in C..... II'm using a service API call which just returns time in GMT and not my time zone...this works to get the actual time and I could do math to say here is GMT hour and here is my HOUR so here is the offset but still a pain... feel free to move this to a comment and I can mark it as the answer. – BostonMacOSX Mar 15 '23 at 16:34
  • @SteveSummit guess tm_gmtoff is not contained in the Time compiled for ESP32/Arduino C – BostonMacOSX Mar 15 '23 at 16:47
  • 1
    @BostonMacOSX I don't mean to be harsh here, but you quoted some words you found on the Internet. How about a link to where you got them from? There's no way for anyone trying to help you to evaluate their context. A lot of what you've been posting about here is valid for POSIX-compliant systems, not the ESP32. That's why I asked for a link. – romkey Mar 15 '23 at 19:23

2 Answers2

-1

[This is a generic, C-heavy answer, not necessarily specific to a particular platform, let alone Arduino.]

I guess there are two questions here: how can you set the timezone, and how can you confirm what it's set to?

It's true that setting the time zone — like, sadly, many aspects of date and time handling in C — is pretty rinky-dink. The recommended way is indeed to set the TZ environment variable, either before your program starts, or from within it by calling setenv or putenv, followed by tzset.

As a comment pointed out, setting the environment like that is questionable on a bare-metal platform like Arduino.

Other than setting the TZ variable, your only other option for choosing the time zone to use involves the "BSD-inspired" tzalloc and localtime_rz functions, as described in this answer. But those functions are, also sadly, not at all widely available.

And then, once you've set the time zone (if you set it), how do you find out what it is?

You found some text documenting some global variables timezone, daylight, and tzname, but those are very, very old, and (IMO) totally obsolete. (But I might be wrong.) In a comment I suggested that "they may still exist in some traditional Unix environments" but that "I'd be surprised if they exist anywhere else". Well, I'm surprised: they exist right here on my Mac! And on Linux, too. But I'm not surprised if they don't exist on Arduino.

If you're not having any luck accessing a global variable named timezone, it would be worth trying _timezone and __timezone; there's a (slight) chance one of those might work.

But the much better way of discovering UTC offsets, which I recommend, is to use the semistandard tm_gmtoff field in struct tm. That is, after calling

struct tm *tmp = localtime(&t);

see if you can access tmp->tm_gmtoff along with tmp->tm_hour and tmp->tm_min and the rest. The tm_gmtoff field isn't standard, unfortunately, but in my experience most systems support it. If tm_gmtoff doesn't work, try _tm_gmtoff and __tm_gmtoff.

And, finally, if neither timezone nor tm_gmtoff are available to you, there's one last trick — try this squirrelly-looking code:

struct tm *tm2 = gmtime(&t);
tm2->tm_isdst = -1;
time_t t2 = mktime(tm2);
printf("UTC offset: %ld\n", t - t2);

This takes a time_t timestamp (that is, seconds since 1970), converts it to broken-down struct tm values without applying the local time zone offset, then converts it from struct tm right back to time_t while applying the local time zone, meaning that the two time_t values should differ by precisely the local time zone offset. Try that, it almost always works. (Even though it is, as I said, squirrelly, and not strictly portable.)

The remaining issue here is what we even mean by "the current time zone". Both tm_gmtoff and the gmtime/mktime trick I just showed will give you the UTC offset as of the time value you're converting, which means you'll get an offset including Daylight Saving Time adjustments. For example, when I run the code just now, those two methods give me an offset of 14400, or 4 hours, because I'm in the U.S. Eastern time zone, and DST just kicked in here last weekend. The timezone variable, on the other hand, contains 18000, or 5 hours, because Eastern time is nominally UTC-5 (except it's 5 hours behind UTC for much less than half the year, due to DST political follies).

Also, the timezone variable (at least on this system) seems to use the opposite sign convention.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • Bro, Arduino does not include the C or C++ standard libraries. It is a baremetal platform based on GCC but those libraries are not included. – Something Something Mar 16 '23 at 02:35
  • Ignore Henrique's comment. Espressif uses newlib which [could support `tm_gmtoff`](https://github.com/espressif/newlib-esp32/blob/12fd7ea6b9fd2d13abe6874c583767b5b8844acd/winsup/cygwin/include/cygwin/config.h#L80), unfortunately they haven't enabled it. So the squirrelly-looking code is probably the best option. See [Espressif documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system_time.html#get-current-time) on timezones and sample code (nothing on _getting_ the TZ, unfortunately, only setting it). – Tarmo Mar 16 '23 at 22:32
  • @Tarmo is confused. newlib is just a small C library for embedded with tiny set of features. This ExpressIf is just a commercial vendor that works on a particular model of Arduino. It is the exception, not the rule. – Something Something Mar 17 '23 at 03:18
  • @Tarmo You can look at [here in the Arduino's official docs](https://www.arduino.cc/reference/en/libraries/) there is no mention of anything you said. – Something Something Mar 17 '23 at 03:21
-1

Although Arduino includes a full C++ compiler (GCC) which comes with the C++ standard library (libstdc++), Arduino does not have an operating system and almost consequently, does not include the standard C library, which is the one you are referring to here.

There are some specialized libraries like this one that implement parts of that functionality but they are far from standard.

Something Something
  • 3,999
  • 1
  • 6
  • 21
  • For anyone reading, please have a look at the [official Arduino docs](https://www.arduino.cc/reference/en/libraries/) to confirm. There is no standard C library in there. What @tarmo is mentioning is a commercial implementation. You could **hack** the arduino install to [add this ExpressIf](https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/) but it will only work with especial hardware. – Something Something Mar 17 '23 at 03:22