How to fix UTC errors in local time in C?

This is a simple question, but the solution seems far from simple. I would like to know how to convert from UTC to local time. I am looking for a solution in the C standard, which is more or less guaranteed to work on any computer anywhere.

I carefully read the following links, but I can not find a solution there:

Convert string containing localtime to UTC in C

Convert between local times and GMT / UTC in C / C ++

I tried several options, for example (datetime is a string with time and date in UTC):

strptime(datetime, "%A %B %d %Y %H %M %S", tp); strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp); 

or

 strptime(datetime, "%A %B %d %Y %H %M %S", tp); lt=mktime(tp); printtime=ctime(&lt); 

No matter what I do, the print time ends with the same as UTC.

Edit 11-29-2013 : based on a very useful answer from the "R" below, I finally got together to create a working example. I found this to work correctly in the two time zones I tested, CET and PST:

 #include <time.h> #include <stdio.h> #include <stdlib.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() { time_t utc, local; char buf[100]; const char datetime[]="2013 11 30 23 30 26 UTC"; /* hard coded date and time in UTC */ struct tm *tp=malloc(sizeof(struct tm)); if(tp==NULL) exit(-1); struct tm *localt=malloc(sizeof(struct tm)); if(localt==NULL) exit(-1); memset(tp, 0, sizeof(struct tm)); memset(localt, 0, sizeof(struct tm)); printf("UTC date and time to be converted in local time: %s\n", datetime); /* put values of datetime into time structure *tp */ strptime(datetime, "%Y %m %d %H %M %S %z", tp); /* get seconds since EPOCH for this time */ utc=mktime(tp); printf("UTC date and time in seconds since EPOCH: %d\n", utc); /* lets convert this UTC date and time to local date and time */ struct tm e0={ .tm_year = 70, .tm_mday = 1 }, e1, new; /* get time_t EPOCH value for e0 (Jan. 1, 1970) */ time_t pseudo=mktime(&e0); /* get gmtime for this value */ e1=*gmtime(&pseudo); /* calculate local time in seconds since EPOCH */ e0.tm_sec += utc - diff_tm(&e1, &e0); /* assign to local, this can all can be coded shorter but I attempted to increase clarity */ local=e0.tm_sec; printf("local date and time in seconds since EPOCH: %d\n", local); /* convert seconds since EPOCH for local time into localt time structure */ localt=localtime(&local); /* get nicely formatted human readable time */ strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S %Z", localt); printf("local date and time: %s\n", buf); } 

It should compile without problems on most systems. I hardcoded the time and date in UTC, which will then be converted to local time and date.

+7
source share
9 answers

If you can consider POSIX (and therefore the POSIX time_t specification as seconds from an era), I would first use the POSIX formula to convert to seconds from an era:

 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 

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

Edit: Actually, the only POSIX dependency has a known era, which corresponds to (time_t)0 . Perhaps you can find a way around this if you really need to ... for example, using calls from both gmtime and localtime at time_t 0 ..

Edit 2: 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 ignore the ugly output code. This program takes an argument in the form of "seconds relative to the POSIX era" and outputs the resulting time to local time. You can convert any UTC time in seconds from an era using the above formula. Note that this code is in no way dependent on POSIX, but it assumes that the offset returned by diff_tm in combination with the second value since the epoch does not overflow int . The fix for this would be to use the long long offset and a loop that will continue to add increments no more than INT_MAX/2 (or less than INT_MIN/2) ) and call mktime to renormalize until the offset reaches 0.

+6
source

Ah ... I could be new to 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; } 

What produces this conclusion:

Local timestamp: 1412554119

Time by Time: 1412546919

Hours difference: 2

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

One note to your code, aseq: you use malloc unnecessarily here (you can also commit values ​​on the stack, and malloc can be expensive, while stack allocation is often much faster) and you do not free it. This is a very, very bad practice.

Another thing:

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

It would be better if you pass sizeof (* tp) (or, if you push tp on the stack, sizeof (tp)) in a memset. This ensures that even if the type of your object changes, it will still be completely memset.

+4
source

To summarize: converting the breakdown date (struct tm) to UTC to (local) calendar time (time_t) is achieved using timegm () - on the contrary mktime () - BUT timegm () is not a standard function (as it is logical). The C standard leaves us with only time (), gmtime (), mktime (), and difftime ().

The workaround found in other docs advises emulating timegm () by setting the TZ environment variable to an empty string first, then calling mktime (), leading to UTC calendar time, and then resetting TZ to its initial value, but again, this not standard.

Basically, as I understand it, the difference between local time and UTC is just an offset, so if we can estimate this offset, we can tune the result of mktime (), so here is my suggestion:

 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); } 

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; } 
+3
source
 //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;} } } 
+2
source

I found that the solution provided by the OP does not work in cases where DST is applied. For example, in my case, DST is currently not valid, but if I set the start date, which should be converted to local time using DST, then this would not work, that is, today is 3/1/2018 and DST is not valid but if I set the conversion date, say, 8/1/2018 0:00:00 when DST is in effect, then the specified decision will be converted to local time, but will not accept DST in the Account. I found that initializing e0 to the date and hour of the original date / time string, and its member tm_isdst - -1 solved the problem. Then I created the following program with additional features that you can include in your code. The original date and time format is the same as what MySQL uses, because I needed this.

 #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; } 
+1
source

I followed @Dachschaden's answer, and I made an example that also displays human-readable output, and I remove the DST parameter for the difference in seconds between GMT and local time. Here he 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 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:

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

Please note that in this case, DST is enabled (there is a difference in the time zone offset in the time zone between UTC and LOCAL).

0
source

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

 $ python >>>46800 / 60 / 60 13 

the difference is 13 hours, which is good since my time zone 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
source

A simple and effective way: add (or subtract) the number of seconds between your time zone and UTC (taking into account daylight saving time).

As an example, which worked very well a minute ago, on December 30, 2017, with standard U.S. mountain time (without DST), which is 7 hours less than 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
source
 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); } 
-7
source

All Articles