Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C code to get local time offset in minutes relative to UTC?

Tags:

c

time

utc

offset

I didn't find a trivial way to get the time offset in minutes between the local time and the UTC time.

At first I intended to use tzset() but it doesn't provide the daylight saving time. According to the man page, it is simply an integer different of zero if day light saving is in effect. While it is usually an hour, it may be half an hour in some country.

I would prefer avoiding to compute the time difference between current UTC returned by gmtime() and localtime().

A more general solution would give me this information for a specified location and a positive time_t value, or at least locally.

Edit 1: the use case is to get the right local time offset for https://github.com/chmike/timez. BTW, If you thought libc functions to manipulate time were Ok, read this https://rachelbythebay.com/w/2013/03/17/time/.

Edit 2: the best and simplest solution I have so far to compute the time offset to UTC in minutes is

// Bogus: assumes DST is always one hour
tzset();
int offset = (int)(-timezone / 60 + (daylight ? 60 : 0));

The problem is to determine the real day light saving time.

Edit 3: Inspired by the answer of @trenki, I came up with the following solution. This is a hack in that it tricks mktime() to consider the output of gmtime() as the localtime. The result is inaccurate when the DST change is in the time span between UTC time and localtime.

#include <stdio.h>
#include <time.h>

int main()
{
    time_t rawtime = time(NULL);
    struct tm *ptm = gmtime(&rawtime);
    // Request that mktime() looksup dst in timezone database
    ptm->tm_isdst = -1;                
    time_t gmt = mktime(ptm);
    double offset = difftime(rawtime, gmt) / 60;
    printf("%f\n", offset);
    return 0;
}
like image 359
chmike Avatar asked Sep 06 '15 13:09

chmike


People also ask

How do you calculate UTC offset?

(GMT-5:00) Eastern Time (US & Canada) Add the local time offset to the UTC time. For example, if your local time offset is -5:00, and if the UTC time is shown as 11:00, add -5 to 11. The time setting when adjusted for offset is 06:00 (6:00 A.M.). Note The date also follows UTC format.

How do I get a time zone offset?

Definition and Usage. getTimezoneOffset() returns the difference between UTC time and local time. getTimezoneOffset() returns the difference in minutes. For example, if your time zone is GMT+2, -120 will be returned.

Does UTC have an offset?

A UTC offset is the difference in hours and minutes between a particular time zone and UTC, the time at zero degrees longitude. For example, New York is UTC-05:00, which means it is five hours behind London, which is UTC±00:00.


2 Answers

Here is my way:

time_t z = 0;
struct tm * pdt = gmtime(&z);
time_t tzlag = mktime(pdt);

Alternative with automatic, local storage of struct tm:

struct tm dt;
memset(&dt, 0, sizeof(struct tm));
dt.tm_mday=1; dt.tm_year=70;
time_t tzlag = mktime(&dt);

tzlag, in seconds, will be the negative of the UTC offset; lag of your timezone Standard Time compared to UTC:

LocalST + tzlag = UTC

If you want to also account for "Daylight savings", subtract tm_isdst from tzlag, where tm_isdst is the field for a particular local time struct tm, after applying mktime to it (or after obtaining it with localtime ).

Why it works:
The set struct tm is for "epoch" moment, Jan 1 1970, which corresponds to a time_t of 0. Calling mktime() on that date converts it to time_t as if it were UTC (thus getting 0), then subtracts the UTC offset from it in order to produce the output time_t. Thus it produces negative of UTC_offset.

like image 56
Victor Avatar answered Oct 21 '22 12:10

Victor


Does your system's strftime() function support the %z and %Z specifiers? On FreeBSD,

 %Z    is replaced by the time zone name.

 %z    is replaced by the time zone offset from UTC; a leading plus sign
       stands for east of UTC, a minus sign for west of UTC, hours and
       minutes follow with two digits each and no delimiter between them
       (common form for RFC 822 date headers).

and I can use this to print this:

$ date +"%Z: %z"
CEST: +0200

ISO C99 has this in 7.23.3.5 The strftime function:

%z     is replaced by the offset from UTC in the ISO 8601 format
       ‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich),
       or by no characters if no time zone is determinable. [tm_isdst]
%Z     is replaced by the locale’s time zone name or abbreviation, or by no
       characters if no time zone is determinable. [tm_isdst]
like image 40
Jens Avatar answered Oct 21 '22 11:10

Jens