Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C API to return timestring in specified time zone?

Tags:

c

timezone

In C, there is any API which converts time returned by time() function to a specific timezone? There is a function strftime() which converts into current timezone of the system. But what I want is function input is time (returned by time()) and timeZone and it will convert the specific formatted time in said timezone format.

Is there such an API?

like image 612
u_peerless Avatar asked Apr 30 '12 03:04

u_peerless


1 Answers

POSIX specifies tzset():

The tzset() function shall use the value of the environment variable TZ to set time conversion information used by ctime, localtime, mktime, and strftime. If TZ is absent from the environment, implementation-defined default timezone information shall be used.

So, you could in theory use something like this to convert t0 (a time_t) into US/Eastern time when that is not your default TZ:

char old_tz[64];
strcpy(old_tz, getenv("TZ"));
setenv("TZ", "EST5", 1);
tzset();
struct tm *lt = localtime(&t0);
char buffer[64];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
setenv("TZ", old_tz, 1);
tzset();

This preserves a copy of the old time zone (you can't rely on it not changing or going out of scope while you're dinking with this stuff), then sets the target time zone in the environment. It then tells the system to look at $TZ and set the local time zone accordingly. You then convert the given time_t value to a broken down local time with localtime(), format the string, then reset the TZ environment variable and tell the system to take another look at it with tzset() again.

In practice, this may or may not work; it depends on the system.

If you need thread-safe versions of this stuff, you'll have to work harder. This is categorically not thread-safe as written.

The sample code has skimped on error checking — it doesn't have any.

I would never claim this is a simple way of doing it; there ought to be a better way.

Test Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static void time_convert(time_t t0, char const *tz_value)
{
    char old_tz[64];
    strcpy(old_tz, getenv("TZ"));
    setenv("TZ", tz_value, 1);
    tzset();
    char new_tz[64];
    strcpy(new_tz, getenv("TZ"));
    char buffer[64];
    struct tm *lt = localtime(&t0);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
    setenv("TZ", old_tz, 1);
    tzset();
    printf("%ld = %s (TZ=%s)\n", (long)t0, buffer, new_tz);
}

int main(void)
{
    time_t t0 = time(0);
    char *tz = getenv("TZ");
    time_convert(t0, tz);
    time_convert(t0, "UTC0");
    time_convert(t0, "IST-5:30");
    time_convert(t0, "EST5");
    time_convert(t0, "EST5EDT");
    time_convert(t0, "PST8");
    time_convert(t0, "PST8PDT");
}

Test Output

1335761328 = 2012-04-29 23:48:48 (TZ=CST6CDT)
1335761328 = 2012-04-30 04:48:48 (TZ=UTC0)
1335761328 = 2012-04-30 10:18:48 (TZ=IST-5:30)
1335761328 = 2012-04-29 23:48:48 (TZ=EST5)
1335761328 = 2012-04-30 00:48:48 (TZ=EST5EDT)
1335761328 = 2012-04-29 20:48:48 (TZ=PST8)
1335761328 = 2012-04-29 21:48:48 (TZ=PST8PDT)

Note that when the strings for EST and PST do not have a daylight saving code specified, you get one time zone offset; when there is a daylight saving code set (such as "EST5EDT" or "PST8PDT") and the time to be printed is at the appropriate time of year (such as end of April), you get the EDT or PDT time value printed.

Note, too, that there are conflicting ISO standards on how to handle time zones. ISO 8601 (for date/time formatting) says that time zones east of UTC (Greenwich) are positive and those west of UTC are negative. At least some other standards (e.g. SQL, ISO 9075) use the same notation. On the other hand, POSIX (aka ISO 9945) uses the opposite convention in TZ, as shown in the example code. The conflict is accepted only because of long-standing precedent. The C standard is silent on the format (or existence) of the TZ environment variable (though C99 §7.23.3.5 The strftime function does reference ISO 8601 a number of times).

And (just as a salutary reminder why error checking is a good idea), on Mac OS X 10.7.3, my environment doesn't have TZ set in it (but it is normally US/Pacific, aka Cupertino, aka America/Los_Angeles, time). So the code above crashes when getenv() returns a null pointer. I ran it with TZ=CST6CDT in the environment, as you can see from the first line of output.

like image 135
Jonathan Leffler Avatar answered Oct 27 '22 06:10

Jonathan Leffler