Search code examples
cutcepoch

Conversion of UTC Date and Time to UNIX Time gives wrong (time zone different) value. Why?


I wrote a small function to convert Date & Time in UTC to UNIX time (Epoch time). The value that I get, however, is dependent on the time-zone I am in.

Here is the code

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

time_t GenerateUnixTimeStampFromDateAndTime(char *DateAndTime);

void main()
{
    long int UnixTime=0;
    char *CurrentTime="01/22/2019 06:30:00";

    UnixTime = (long int)GenerateUnixTimeStampFromDateAndTime(CurrentTime);
    printf("Current Unix Time= %ld\r\n", UnixTime);
}

time_t GenerateUnixTimeStampFromDateAndTime(char *DateAndTime) 
{
    struct tm ti={0};
    if( sscanf(DateAndTime, "%d/%d/%d %d:%d:%d", &ti.tm_mon, &ti.tm_mday, &ti.tm_year, &ti.tm_hour, &ti.tm_min, &ti.tm_sec) != 6 )
        return -1;
    ti.tm_year = ti.tm_year - 1900;
    ti.tm_mon = ti.tm_mon - 1;

    return mktime(&ti);
}

The answer I get is 1548118800 which is 01/22/2019 01:00:00 ie -5:30 which is the time-zone I am in (India). If I change my PC timezone to UTC, then it gives correct value of 1548138600.

What changes do i need to make it time-zone independent?


Solution

  • As documented, mktime() takes the broken-down time components in local time.

    First, save the users current timezone:

    char  *old_timezone, *temp;
    
    temp = getenv("TZ");
    if (temp) {
        const size_t  len = temp;
        old_timezone = malloc(len + 1);
        if (!old_timezone) {
            fprintf(stderr, "Out of memory!\n");
            exit(EXIT_FAILURE);
        }
        if (len > 0)
            memcpy(old_timezone, temp, len);
        old_timezone[len] = '\0';
    } else
        old_timezone = NULL;
    

    If the user uses the system default, old_timezone will be NULL.

    Next, set the current timezone (for this process) to UTC:

    setenv("TZ", "UTC", 1);
    tzset();
    

    Note that if you want, you can use any timezone specifier instead of "UTC" above; see tzset() for details. The tzset() call is usually done internally by your C library, but doing it explicitly here helps us humans see that something timezone-specific just occurred.

    At this point, mktime() will operate in UTC, and localtime() and gmtime() will return the same results.

    Afterwards, restore the timezone by

    if (old_timezone) {
        setenv("TZ", old_timezone, 1);
        free(old_timezone);
        old_timezone = NULL;
    } else
        unsetenv("TZ");
    tzset();
    

    Note that this does not affect anything except the current process (and any child processes you might create via popen() or system() or fork() and exec()). Timezone, like locale, is a per-process property.


    Running

        unsetenv("TZ");
        tzset();
    

    will change the current timezone for this process to the system default timezone.


    If your program operates explicitly in UTC, you can do just

        /* This program works explicitly in the UTC timezone.
           User/system timezone configuration is completely ignored. */
        setenv("TZ", "UTC", 1");
        tzset();
    

    near the beginning of your main().