How to convert from UTC to local time in C?

前端 未结 10 1591
无人共我
无人共我 2020-12-05 20:44

It\'s a simple question, but the solution appears to be far from simple. I would like to know how to convert from UTC to local time. I am looking for a solution in C that\'s

相关标签:
10条回答
  • 2020-12-05 21:28

    try this, test output: utcEpochTime: 1487652688, localEpochTime: 1487699488, diff: 46800

    $ python
    >>>46800 / 60 / 60
    13
    

    the diff is 13 hours, which is good, as my timezone is UTC+8.

    #include <stdio.h>
    #include <time.h>
    
    int main(int argc, char *argv[])
    {
        time_t utcEpochTime = time(0);
        time_t localEpochTime = 0;
    
        struct tm tm = {0};
        localtime_r(&utcEpochTime, &tm);
        tm.tm_isdst = -1;
        localEpochTime = timegm(&tm);
    
        printf("utcEpochTime: %d, localEpochTime: %d, diff: %d\n", (int)utcEpochTime, (int)localEpochTime, (int)(localEpochTime - utcEpochTime));
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-05 21:30

    To sum-up: the conversion of a broken down date (struct tm) in UTC to a (local) calendar time (time_t) is achieved with timegm() - the opposite of mktime() - BUT timegm() is not a standard function (how logic is that). The C standard leaves us with only time(), gmtime(), mktime() and difftime().

    A workaround found in other docs advises to emulate timegm() by setting first the environment variable TZ to a null string, then calling mktime() resulting in an UTC calendar time, then resetting TZ to its initial value, but once again, this is not standard.

    Basically, as I understand it, the difference between a local time and UTC time is just an offset so if we can evaluate that offset, we can adjust the result of mktime(), so here's my proposition:

    time_t my_timegm(struct tm *tm) {
        time_t epoch = 0;
        time_t offset = mktime(gmtime(&epoch));
        time_t utc = mktime(tm);
        return difftime(utc, offset);
    }
    

    A quick test:

    int main(void) {
        time_t now = time(0);
        struct tm local = *localtime(&now);
        struct tm utc = *gmtime(&now);
        time_t t1 = mktime(&local);
        time_t t2 = my_timegm(&utc);
        assert(t1 == t2);
        printf("t =%lu\nt1=%lu\nt2=%lu\n",now,t1,t2);
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-05 21:30

    I found that the solution the OP gave did not work in cases when DST applies. For example, in my case, at the current time, DST was not in effect, but if I set the initial date which should convert to local time with DST, then it would not work, i.e. today's date is 3/1/2018 and DST is not in effect, but if I set the date for conversion to, say, 8/1/2018 0:00:00 when DST is in effect, then the solution given would convert to local time, but would not take DST into account. I found that initializing e0 to the date and hour of the initial date/time string and its member tm_isdst to -1 solved the problem. I then created the following program with complementary functions which you can include in your code. The initial format of the date and time is the same that MySQL uses, because I needed it for such purposes.

    #include <stdio.h>
    #include <time.h>
    #include <string.h>
    
    long long diff_tm(struct tm *a, struct tm *b) {
     return a->tm_sec - b->tm_sec
          + 60LL * (a->tm_min - b->tm_min)
          + 3600LL * (a->tm_hour - b->tm_hour)
          + 86400LL * (a->tm_yday - b->tm_yday)
          + (a->tm_year - 70) * 31536000LL
          - (a->tm_year - 69) / 4 * 86400LL
          + (a->tm_year - 1) / 100 * 86400LL
          - (a->tm_year + 299) / 400 * 86400LL
          - (b->tm_year - 70) * 31536000LL
          + (b->tm_year - 69) / 4 * 86400LL
          - (b->tm_year - 1) / 100 * 86400LL
          + (b->tm_year + 299) /400 * 86400LL;
    }
    
    void localToUTC(char *buf, const char *localTime) {
     struct tm tp;
     strptime(localTime, "%Y-%m-%d %H:%M:%S", &tp);
     tp.tm_isdst = -1;
     time_t utc = mktime(&tp);
     struct tm res = *gmtime(&utc);
     strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &res);
    }
    
    void utcToLocal(char *buf, const char *utcTime) {
     struct tm tp;
     strptime(utcTime, "%Y-%m-%d %H:%M:%S", &tp);
     tp.tm_isdst = -1;
     time_t utc = mktime(&tp);
     struct tm e0 = { .tm_year = tp.tm_year, .tm_mday = tp.tm_mday, .tm_mon = tp.tm_mon, .tm_hour = tp.tm_hour, .tm_isdst = -1 };
     time_t pseudo = mktime(&e0);
     struct tm e1 = *gmtime(&pseudo);
     e0.tm_sec += utc - diff_tm(&e1, &e0);
     time_t local = e0.tm_sec;
     struct tm localt = *localtime(&local);
     strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &localt);
    }
    
    int main(void) {
     char mytime_1[20] = "2018-02-28 13:00:00";
     char utctime_1[20], back_1[20];
     localToUTC(utctime_1, mytime_1);
     utcToLocal(back_1, utctime_1);
     printf("My time: %s\n", mytime_1);
     printf("UTC time: %s\n", utctime_1);
     printf("Back: %s\n", back_1);
    
     printf("-------------------------------------------\n");
    
     char mytime_2[20] = "2018-07-28 17:00:00";
     char utctime_2[20], back_2[20];
     localToUTC(utctime_2, mytime_2);
     utcToLocal(back_2, utctime_2);
     printf("My time: %s\n", mytime_2);
     printf("UTC time: %s\n", utctime_2);
     printf("Back: %s\n", back_2);
    
     printf("-------------------------------------------\n");
    
     return 0;
    }
    
    0 讨论(0)
  • 2020-12-05 21:32

    I think it's easier than that; time.h defines three variables:

    extern int    daylight;
    extern long   timezone;
    extern char  *tzname[];
    

    which are loaded based on the TZ env variable when you call

     tzset();
    

    if you have a utc time in

    struct tm date;
    date.tm_isdst = 0;
    

    convert it to a time_t using mktime

    time_t utc = mktime( &date );
    

    then convert it to local time

    time_t local = utc - timezone + ( daylight?3600:0 );
    

    timezone is the number of seconds away from utc for the current timezone and daylight is 1 to indicate daylight savings time is in play and zero for not.

    A small caution: When I coded this for a microcontroller and cross compiled, it's time.h defined those variables with initial underscores.

    See the man page for time.h

    0 讨论(0)
提交回复
热议问题