Converting string containing localtime into UTC in C
Solution 1
I think I've cracked it now, thanks to Andomar - this code does what I need and appears to work regardless of the current DST status (I changed the clock on my PC to check this):
#include <time.h>
#include <assert.h>
time_t parseLocalDate(char* date){
struct tm cal = {0, 0, 0, 0, 0, 0, 0, 0, -1, 0, NULL};
strptime(date, "%Y/%m/%d/%H", &cal);
return mktime(&cal);
}
int main(int argc, char *argv[]){
// DST is effect, Local Time = GMT+1
assert(1251759600 == parseLocalDate("2009/09/01/00")); // Mon, 31 Aug 2009 23:00:00 GMT
assert(1254351600 == parseLocalDate("2009/10/01/00")); // Wed, 30 Sep 2009 23:00:00 GMT
// DST not in effect, Local Time = GMT
assert(1257033600 == parseLocalDate("2009/11/01/00")); // Sun, 01 Nov 2009 00:00:00 GMT
}
Solution 2
You can use mktime
to interpret a struct tm in the local timezone. When you do so, be careful to set the tm_isdst
flag. It's 0 for summertime, 1 for wintertime, and to -1 to have mktime
figure it out. Here's some example code:
void main()
{
char* date = "2009/09/01/00";
struct tm cal = {};
// Read string into struct tm
strptime(date, "%Y/%m/%d/%H", &cal);
// Tell mktime to figure out the daylight saving time
cal.tm_isdst = -1;
printf("%20s: %s", "Before mktime", asctime(&cal));
// Convert struct tm to time_t
time_t t = mktime(&cal);
// Convert time_t to localtime
struct tm localcal = *localtime(&t);
printf("%20s: %s", "Local time", asctime(&localcal));
printf("%20s: %i\n", "Local DST", localcal.tm_isdst);
// Convert time_t to GMT
struct tm gmcal = *gmtime(&t);
printf("%20s: %s", "GM time", asctime(&gmcal));
printf("%20s: %i\n", "GM DST", gmcal.tm_isdst);
}
This prints (I live in GMT+1, and it's wintertime now):
Before mktime: Tue Sep 1 00:00:00 2009
Local time: Tue Sep 1 00:00:00 2009
Local DST: 1
GM time: Mon Aug 31 22:00:00 2009
GM DST: 0
It looks like mktime
converts a date in September based on the current daylight savings time. It's November now, so it's actually one hour off. I haven't found a way to correct that.
codebox
Updated on June 13, 2022Comments
-
codebox almost 2 years
I have a string containing a local date/time and I need to convert it to a time_t value (in UTC) - I've been trying this:
char* date = "2009/09/01/00"; struct tm cal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL}; strptime(date, "%Y/%m/%d/%H", &cal); time_t t = mktime(&cal);
but the time_t value I get back is the value that I would expect if the string was being parsed as UTC not local time. Maybe I have misunderstood what strptime is supposed to do, but in my timezone (UK) on the 1st September we are using BST (ie UTC + 1 hour) so I would expect the value I end up with to be 1 hour ahead of UTC.
Is there a way to interpret the string as localtime, automatically taking into account the UTC offset that would have been in effect on that date? Note that I need the time_t value not a struct tm, in the example above I want the time_t value to correspond to 2009-09-01 01:00:00 GMT
-
codebox over 14 yearsthanks, but its the time_t value that I'm interested in - if I do a mktime() on the 'localcal' struct in your code it gives me the same result as doing mktime() on 'cal'. I need some way to produce a time_t value equivalent to '2009-09-01 01:00 GMT'
-
Andomar over 14 yearsThe C convention is to store time_t as the number of seconds since 1970-01-01 in UTC. It's changed it to localtime only for interpretation or display. If you'd like to introduce rob_t as the localtime variant, you can calculate the number of seconds between gmtime() and localtime() and add that to your time_t. :)
-
codebox over 14 yearsok, but I want to interpret the string as local time, not produce a localtime equivalent of a UTC time string. In our example, assuming that we live in GMT+1, the string "2009/09/01/00" is our local time, and so it is equivalent to "2009/09/01/01" in GMT, and so the code should return the time_t value corresponding to "2009/09/01/01". Does that make sense?
-
Andomar over 14 yearsRight, this stuff is complex. Turns out
mktime()
does expect a localtime struct. The 1 hour difference in my example was the daylight saving time, which was set to 0 incal
, but is1
at the moment. I'll edit the example. -
Andomar over 14 yearsThe
struct tm
you pass tomktime
hastm_isdst
set to 0, so the time is read without DST. Then you pass it toctime
, which does correct for DST by adding 1 hour. Next you subtract the offset, which is confusingly also 1 hour, to get some random time. This stuff makes my head spin but I'd like to get to the bottom of it :) -
pmg over 14 yearsThis DST is funny business. I just realized that DST is not in effect currently, but it was on the 1st of September! Hmmm ... I guess there's no way for the library to "know" when DST starts and ends at a specific place. I think you have to manage DST by hand ( timeanddate.com/time/dst2009.html )
-
lanoxx over 5 yearsIn cases where you do not want the conversion to local time you can use the UTC equivalent for
mktime
which istimegm
.