How to convert from UTC to local time in C?

前端 未结 10 1590
无人共我
无人共我 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:07

    If you can assume POSIX (and thus the POSIX specification of time_t as seconds since the epoch), I would first use the POSIX formula to convert to seconds since the epoch:

    tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
        (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
        ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
    

    Next, use localtime((time_t []){0}) to get a struct tm representing the epoch in local time. Add the seconds since the epoch to the tm_sec field of this struct tm, then call mktime to canonicalize it.

    Edit: Actually the only POSIX dependency is having a known epoch which (time_t)0 corresponds to. Perhaps you can find a way around that if you really need to... for instance using calls to both gmtime and localtime at time_t 0..

    Edit 2: A sketch of how to do this:

    #include <time.h>
    #include <stdio.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;
    }
    
    int main(int argc, char **argv)
    {
            char buf[100];
            struct tm e0 = { .tm_year = 70, .tm_mday = 1 }, e1, new;
            time_t pseudo = mktime(&e0);
            e1 = *gmtime(&pseudo);
            e0.tm_sec += atoi(argv[1]) - diff_tm(&e1, &e0);
            mktime(&e0);
            strftime(buf, sizeof buf, "%c", &e0);
            puts(buf);
    }
    

    Please don't mind the ugly output code. This program takes an argument in the form of "seconds relative to the POSIX epoch" and outputs the resulting time in local time. You can convert any UTC time to seconds since the epoch using the formula I cited above. Note that this code does not in any way depend on POSIX, but it does assume the offset returned by diff_tm combined with the seconds-since-the-epoch value does not overflow int. A fix for this would be to use a long long offset and a loop that keeps adding increments no larger than INT_MAX/2 (or smaller than INT_MIN/2) and calling mktime to renormalize until the offset reaches 0.

    0 讨论(0)
  • 2020-12-05 21:07

    A simple and effective way: Add (or subtract) the number of seconds between your time zone and UTC (considering daylight saving time).

    As an example that worked just fine a minute ago, on December 30, 2017, with U.S. Mountain Standard Time (no DST), which is 7 hours behind UTC:

    time_t     current_time_UTC;
    time_t     current_time_MST;
    
    struct tm *current_broken_time_MST;
    
    uint32_t seven_hours_in_seconds = 25200; // Get this any way you want
    
    current_time_UTC = time (NULL);                                 // UTC
    current_time_MST = current_time_UTC - seven_hours_in_seconds;   // MST
    
    current_broken_time_MST = localtime (&current_time_MST);        // MST
    

    Enjoy.

    0 讨论(0)
  • 2020-12-05 21:16
    void   CTestDlg::OnBtnTest()   
    { 
    HANDLE   hFile; 
    WIN32_FIND_DATA   wfd; 
    SYSTEMTIME   systime; 
    FILETIME   localtime; 
    char   stime[32];     //
    memset(&wfd,   0,   sizeof(wfd)); 
    
    if((hFile=FindFirstFile( "F:\\VC\\MFC\\Test\\Release\\Test.exe ",        &wfd))==INVALID_HANDLE_VALUE) 
    { 
    char   c[2]; 
    DWORD   dw=GetLastError(); 
    wsprintf(c,   "%d ",   dw); 
    AfxMessageBox(c);   
    return   ;//
    } 
    FileTimeToLocalFileTime(&wfd.ftLastWriteTime,&localtime); 
    FileTimeToSystemTime(&localtime,&systime); 
    sprintf(stime, "%4d-%02d-%02d   %02d:%02d:%02d ", 
          systime.wYear,systime.wMonth,systime.wDay,systime.wHour, 
          systime.wMinute,systime.wSecond); 
    AfxMessageBox(stime);   
    } 
    
    0 讨论(0)
  • 2020-12-05 21:19

    I followed the answer by @Dachschaden and I made an example which also shows human-readable output and I remove the DST option for the difference in seconds between UTC and local time. Here it is:

    #include <time.h>
    #include <stdio.h>
    
    #define DATE_MAX_STR_SIZE 26
    #define DATE_FMT "%FT%TZ%z"
    
    int main() {
    
        time_t now_time, now_time_local;
        struct tm now_tm_utc, now_tm_local;
        char str_utc[DATE_MAX_STR_SIZE];
        char str_local[DATE_MAX_STR_SIZE];
    
        time(&now_time);
        gmtime_r(&now_time, &now_tm_utc);
        localtime_r(&now_time, &now_tm_local);
    
        /* human readable */
        strftime(str_utc, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_utc);
        strftime(str_local, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_local);
    
        printf("\nUTC: %s", str_utc);
        printf("\nLOCAL: %s\n", str_local);
    
        /* seconds (epoch) */
        /* let's forget about DST for time difference calculation */
        now_tm_local.tm_isdst = 0;
        now_tm_utc.tm_isdst = 0;
        now_time_local = now_time + (mktime(&now_tm_local) - mktime(&now_tm_utc));
    
        printf("\nUTC in seconds: %lu", now_time);
        printf("\nLOCAL in seconds: %lu\n", now_time_local);
    
        return 0;
    }
    

    Output on my machine is:

    UTC: 2016-05-05T15:39:11Z-0500
    LOCAL: 2016-05-05T11:39:11Z-0400
    
    UTC in seconds: 1462462751
    LOCAL in seconds: 1462448351
    

    Note that DST is on in this case (there's a 1 hour time zone offset difference between UTC and LOCAL).

    0 讨论(0)
  • 2020-12-05 21:20
    //working stand alone function adjusting UTC to local date and time
    //globals(unsigned integers): gps.Mth, gps.Yr, gps.Hm (eg:2115 for 21:15)
    //adjust date and time according to UTC
    //tz(timezone) eg: 1100, for 11 hours, tzdir: 1 forward, 0 backwards            
    
    
    
    
    
        void AdjustUTCToTimeZone(u16 tz, u8 tzdir){
        u8 maxDayInAnyMonth[13] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //gps.Mth 1-12 (not zero)
            if(gps.Yr%4==0){maxDayInAnyMonth[2]=29;}//adjust for leapyear
            u8 maxDayUtcMth =maxDayInAnyMonth[gps.Mth];
            u8 maxDayPrevMth=maxDayInAnyMonth[gps.Mth-1];
            if(!maxDayPrevMth){maxDayPrevMth=31;} //month before utc month
    
            u16 hr=(gps.Hm/100)*100;u16 m=gps.Hm-hr;  //2115 --> 2100 hr and 15 min
            if(tzdir){//adjusting forwards
                tz+=gps.Hm;
                if(tz>2400){gps.Hm=tz-2400;gps.Day++;                //spill over to next day
                      if(gps.Day>maxDayUtcMth){ gps.Day=1;gps.Mth++; //spill over to next month
                          if(gps.Mth>12){gps.Mth=1; gps.Yr++;        //spill over to next year
                          }
                      }
                }else{gps.Hm=tz;}
            }else{//adjusting backwards
                if(tz>gps.Hm){gps.Hm=(2400-(tz-hr))+m;gps.Day--;  // back to previous day
                      if(gps.Day==0){                             //back to previous month
                         gps.Mth--;gps.Day=maxDayPrevMth;
                         if(!gps.Mth){gps.Mth=12;                 //back to previous year
                            gps.Yr--;
                         }
                      }
                }else{gps.Hm-=tz;}
            }
        }
    
    0 讨论(0)
  • 2020-12-05 21:27

    Ahm ... I might just be a beginner in C, but I got this working example:

    #include <time.h>
    #include <stdio.h>
    int main(void)
    {
            time_t abs_ts,loc_ts,gmt_ts;
            struct tm loc_time_info,gmt_time_info;
    
            /*Absolute time stamp.*/
            time(&abs_ts);
    
            /*Now get once the local time for this time stamp,
            **and once the GMT (UTC without summer time) time stamp.*/
            localtime_r(&abs_ts,&loc_time_info);
            gmtime_r(&abs_ts,&gmt_time_info);
    
            /*Convert them back.*/
            loc_ts=mktime(&loc_time_info);
            gmt_ts=mktime(&gmt_time_info);
    
            /*Unfortunately, GMT still has summer time. Get rid of it:*/
            if(gmt_time_info.tm_isdst==1)
                    {gmt_ts-=3600;}
    
            printf("Local timestamp: %lu\n"
                    "UTC timestamp: %lu\n"
                    "Difference in hours: %lu\n\n",
                    loc_ts,
                    gmt_ts,
                    (loc_ts-gmt_ts)/3600);
    
            return 0;
    }
    

    Which produces this output:

    Local timestamp: 1412554119

    GMT timestamp: 1412546919

    Difference in hours: 2

    Now you have the difference between UTC and local time in seconds. That should be enough to convert it.

    One note to your code, aseq: you are using malloc without need here (you can memset values on the stack as well, and malloc can be expensive while stack allocation is often much faster), and you do not free it. That's very, very bad practise.

    Another thing:

    memset(tp, 0, sizeof(struct tm));

    Would be better done if you'd pass sizeof(*tp) (or, if you put tp on the stack, sizeof(tp)) to memset. That ensures that even if the type of your object changes, it will still be fully memset.

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