Given a UTC timestamp, I'd like to determine if DST was active at that time in European Union.
CodePudding user response:
The usual way of answering this question makes use of the comprehensive tz database, which is built into most operating systems, with the exception of Windows, which uses its own, similar database.
A database like this is necessary in order for the C library localtime
and mktime
functions to work correctly.
If you already have a broken-down date and time (year/month/day, hour:minute:second), the function you want is mktime
. Let's try two dates, July 8th and December 13th.
#include <stdio.h>
#include <time.h>
int main()
{
struct tm tm1, tm2;
/* July 8th */
tm1.tm_year = 2022 - 1900;
tm1.tm_mon = 7 - 1;
tm1.tm_mday = 8;
tm1.tm_hour = 12;
tm1.tm_min = tm1.tm_sec = 0;
tm1.tm_isdst = -1;
mktime(&tm1);
printf("at %d:d:d on %d-d-d, DST %s in effect\n",
tm1.tm_hour, tm1.tm_min, tm1.tm_sec,
tm1.tm_year 1900, tm1.tm_mon 1, tm1.tm_mday,
tm1.tm_isdst ? "was" : "was not");
/* December 13th */
tm2.tm_year = 2022 - 1900;
tm2.tm_mon = 12 - 1;
tm2.tm_mday = 13;
tm2.tm_hour = 12;
tm2.tm_min = tm2.tm_sec = 0;
tm2.tm_isdst = -1;
mktime(&tm2);
printf("at %d:d:d on %d-d-d, DST %s in effect\n",
tm2.tm_hour, tm2.tm_min, tm2.tm_sec,
tm2.tm_year 1900, tm2.tm_mon 1, tm2.tm_mday,
tm2.tm_isdst ? "was" : "was not");
}
For me this prints
at 12:00:00 on 2022-07-08, DST was in effect
at 12:00:00 on 2022-12-13, DST was not in effect
When filling in struct tm
, there are a couple of things to be careful of, as illustrated in the code above:
- field
tm_year
is offset by 1900 - field
tm_mon
is offset by 1 (it's 0-based) - field
tm_isdst
contains 1 if DST is in effect, 0 if its not, or -1 if we're not sure.
So the strategy is simply: fill in a struct tm
with the desired date/time, set tm_isdst
to -1 indicating that we don't know whether DST is in effect or not, and call mktime
. Among other things, mktime
figures out whether DST was in effect or not, and adjusts tm_isdst
in the pointed-to struct tm
accordingly.
If the "UTC timestamp" you said you had to start with is a Unix-style time_t
value, you can answer your question using the localtime
function, which starts with a time_t
value and constructs a struct tm
:
time_t t = 1670714400;
struct tm *tmp = localtime(&t);
printf("at %d:d:d on %d-d-d, DST %s in effect\n",
tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
tmp->tm_year 1900, tmp->tm_mon 1, tmp->tm_mday,
tmp->tm_isdst ? "was" : "was not");
On my machine this prints
at 18:20:00 on 2022-12-10, DST was not in effect
I've glossed over one important question, which is: how do localtime
and mktime
know which of the world's time zone and daylight savings rules to use? For Unix-like operating systems, at least, it's based on an environment variable, TZ
. Normally it's set to match the part of the world where you are (where your computer is installed), although you can set it to anything you want. For example, if I wanted my program to use the rules for Central European time, I'd set TZ to Europe/Rome
, the tz identifier for that zone.
The usual way of setting environment variables (again, under Unix) is from the shell, outside of your C program. For example, to run my program under a different time zone, I might type
TZ=Europe/Rome
export TZ
./myprogram
Or there's this shortcut:
TZ=Europe/Rome ./myprogram
Or, you can also set an environment variable from within a C program, like this:
setenv("TZ", "Europe/Rome", 1);
tzset();
setenv
is a Posix function, not Standard C. After you change the TZ
environment variable from within a C program like this, you have to call tzset
to let the timezone-related functions like mktime
know you've done so.
CodePudding user response:
Daylight saving time is observed from the last Sunday in March (02:00 CET) to the last Sunday in October (03:00 CEST). (https://en.wikipedia.org/wiki/Summer_time_in_Europe). To test, compare e.g. with https://www.timeanddate.com/time/change/germany?year=2022
#include <stdio.h>
#include <stdlib.h>
//included for reference only
int wd(int y, int m, int d)
{
return (d = m<3?y--:y-2, 23*m/9 d 4 y/4 - y/100 y/400) % 7;
//https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Keith
}
#define GregorianAdpotion 1752
int main(int argc, char *argv[])
{
int y = argc > 1 ? atoi(argv[1]) : 2022;
if (y <= GregorianAdpotion)
return 1;
//https://rosettacode.org/wiki/Find_the_last_Sunday_of_each_month#FreeBASIC
int x = 33 y y/4 - y/100 y/400; //common part of Keith for Mar and Oct
int last_Sun_March = 31 - x % 7;
int last_Sun_Oct = 31 - (4 x) % 7;
printf("DST from %d-03-%d 02:00 CET ", y, last_Sun_March);
printf("to %d-10-%d 03:00 CEST\n", y, last_Sun_Oct);
return 0;
}