Converting string containing localtime into UTC in C

15,113

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.

Share:
15,113
codebox
Author by

codebox

Updated on June 13, 2022

Comments

  • codebox
    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
    codebox over 14 years
    thanks, 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
    Andomar over 14 years
    The 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
    codebox over 14 years
    ok, 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
    Andomar over 14 years
    Right, 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 in cal, but is 1 at the moment. I'll edit the example.
  • Andomar
    Andomar over 14 years
    The struct tm you pass to mktime has tm_isdst set to 0, so the time is read without DST. Then you pass it to ctime, 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
    pmg over 14 years
    This 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
    lanoxx over 5 years
    In cases where you do not want the conversion to local time you can use the UTC equivalent for mktime which is timegm.