Im trying to get the current localtime in C, lets say Italy, I tried the code below but it returns a time three hours earlier than the real one. For example if executed at 17:34 it will return 14:34, what am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
setenv("TZ", "UTC 1", 1); // Italy is UTC 1
tzset();
time_t sec = 1634224877; // Just for test
struct tm * end = localtime(&sec);
// Check for daylight save
if(end->tm_isdst == 1)
{
end->tm_hour = 1;
}
printf("Daylight save time?: %d\n", end->tm_isdst);
printf("Time is says: %.2d/%.2d/%d - %.2d:%.2d:%.2d\n", end->tm_mday, end->tm_mon 1, end->tm_year 1900,
end->tm_hour, end->tm_min, end->tm_sec);
}
Thanks
CodePudding user response:
tl;dr If you want the time at a location, you have to set TZ
to a location.
1634224877 is 2021-10-14 15:21:17 UTC. setenv
takes POSIX time zones which don't work like you think. UTC 1 means one hour head of UTC. That is 14:21:17. If you want "UTC 1" you actually ask for UTC-1
, one hour behind UTC.
But don't ask for UTC-1
.
setenv("TZ", "UTC 1", 1); // Italy is UTC 1
That comment is not correct. Italy is not UTC 1. Sometimes Italy is UTC 1, sometimes it is UTC 2. In order to know about daylight savings time (and other wacky time zone issues), TZ needs to know your location.
Set TZ to the city closest to you, like Europe/Rome
. Now localtime
can figure out whether it's daylight savings time or not, you do not need to correct it.
int main()
{
setenv("TZ", "Europe/Rome", 1);
tzset();
time_t sec = 1634224877; // Just for test
struct tm * end = localtime(&sec);
printf("Daylight savings time?: %d\n", end->tm_isdst);
printf("Time is says: %.2d/%.2d/%d - %.2d:%.2d:%.2d\n", end->tm_mday, end->tm_mon 1, end->tm_year 1900,
end->tm_hour, end->tm_min, end->tm_sec);
}
Daylight savings time?: 1
Time is says: 14/10/2021 - 17:21:17
The system that manages time zones is called tzdata. It is a database of locations, their time zone information, daylight savings time switches, and a host of other wacky time zone information. It's what lets your computer know that Rome is usually UTC 1 but should sometimes be UTC 2.
A list of all tzdata locations can be had on Wikipedia, but these do not necessarily match the tzdata installed on your machine.
CodePudding user response:
The TZ
environment variable value FOO 1
(I changed it from UTC 1
to avoid confusion) is interpreted as a standard time zone designated "FOO" with no alternate (daylight savings) zone. The 1
(the
is optional) means that 1 hour needs to be added to the local time to convert it to Coordinated Universal Time (UTC). To specify an alternate (daylight savings) time zone, it is added after the standard time offset, e.g. FOO 1BAR0
. The offset after the alternate zone can be omitted in which case it defaults to one less than the standard offset, so FOO 1BAR0
can be shortened to FOO1BAR
. This means that local time will be 1 hour behind UTC when standard time is in effect and will be UTC when alternate (daylight savings) time is in effect. Optionally, the offset can include minutes or minutes and seconds, e.g. FOO 01:00BAR 00:00:00
.
Italy uses Central European Time (CET, corresponding to UTC 1) when standard time is in effect (e.g. during winter) corresponding to UTC 1, and uses Central European Summer Time (CEST, corresponding to UTC 1) when alternate (daylight savings) time is in effect (e.g. during summer). That can be expressed by the TZ
environment variable value CET-1CEST-2
or CET-1CEST
. Notice that the offsets used in the TZ
environment variable have the opposite sign to the usual convention.
So far, it is left up to the system libraries to use some arbitrary (and usually incorrect for most of the world) rule to determine the date and time of transitions between standard time and alternate time. Simple rules for the date and time of exactly two transitions per year can be specified after the alternate zone designation and offset, separated by commas. The transition date can be specified as Mm.n.d, meaning the dth day (0 = Sunday, 1 = Monday, ..., 6 = Saturday) of the nth week (counting from 1) of the mth month (1 = January, ..., 12 = December). n = 5 is interpreted as the last d day of the month m. The transition date can also be specified as Jn where n is the day of the year, not counting February 29 (so March 1 is always day 60). The (optional) transition time can be specified as /time, where time specifies the current local time on the transition date at which the transition to the other time is made, defaulting to 02:00:00. The time value can be abbreviated in the same way as time zone offsets, so /2 is the same as /02:00:00, but no leading plus or minus sign is allowed (by the current standard).
Italy currently operates on EU time zone transition rules (the abolition of which is currently postponed), where the transitions occur at 01:00 UTC and local time advances 1 hour on the last Sunday of March (M3.5.0
) and retreats 1 hour on the last Sunday of October (M10.5.0
). For Italy, the local transition times are 02:00:00 in March (advancing to 03:00:00) and 03:00:00 in October (retreating to 02:00:00), so the rules are M3.5.0/2
and M10.5.3
. (M3.5.0/2
can be shortened to M3.5.0
since it uses the default transition time.)
The following, modified code will work to show time in Italy, at least until the current EU time zone rules are abolished:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); // Italy rules!
tzset();
time_t sec;
#if 0
sec = 1634224877; // Just for test
#else
sec = time(NULL); // For current time
#endif
struct tm * end = localtime(&sec);
printf("Daylight save time?: %d\n", end->tm_isdst);
printf("Time is says: %.2d/%.2d/%d - %.2d:%.2d:%.2d\n",
end->tm_mday, end->tm_mon 1, end->tm_year 1900,
end->tm_hour, end->tm_min, end->tm_sec);
}