diff --git a/platform-independent/c/core/src/mpw-marshal-util.c b/platform-independent/c/core/src/mpw-marshal-util.c index 4da45eb5..eca589c9 100644 --- a/platform-independent/c/core/src/mpw-marshal-util.c +++ b/platform-independent/c/core/src/mpw-marshal-util.c @@ -35,10 +35,11 @@ char *mpw_get_token(const char **in, const char *eol, char *delim) { return token; } -time_t mpw_mktime( +time_t mpw_timegm( const char *time) { // TODO: Support for parsing non-UTC time strings + // Parse time as a UTC timestamp, into a tm. struct tm tm = { .tm_isdst = -1 }; if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, @@ -46,8 +47,9 @@ time_t mpw_mktime( tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900 tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1 - // mktime converts tm to local, setting tm_gmtoff; use it to offset the result back to UTC. - return mktime( &tm ) + tm.tm_gmtoff; + // mktime interprets tm as being local, we need to offset back to UTC (timegm/tm_gmtoff are non-standard). + time_t local_time = mktime( &tm ), local_dst = tm.tm_isdst > 0? 3600: 0; + return local_time + local_dst - mktime( gmtime( &local_time ) ); } return false; diff --git a/platform-independent/c/core/src/mpw-marshal-util.h b/platform-independent/c/core/src/mpw-marshal-util.h index 3afbfb07..a4db76b6 100644 --- a/platform-independent/c/core/src/mpw-marshal-util.h +++ b/platform-independent/c/core/src/mpw-marshal-util.h @@ -34,7 +34,7 @@ char *mpw_get_token( const char **in, const char *eol, char *delim); /** Convert an RFC 3339 time string into epoch time. */ -time_t mpw_mktime( +time_t mpw_timegm( const char *time); /// JSON parsing. diff --git a/platform-independent/c/core/src/mpw-marshal.c b/platform-independent/c/core/src/mpw-marshal.c index cee7c948..e598779b 100644 --- a/platform-independent/c/core/src/mpw-marshal.c +++ b/platform-independent/c/core/src/mpw-marshal.c @@ -407,7 +407,7 @@ static void mpw_marshal_read_flat_info( if (strcmp( headerName, "Passwords" ) == 0) info->redacted = strcmp( headerValue, "VISIBLE" ) != 0; if (strcmp( headerName, "Date" ) == 0) - info->date = mpw_mktime( headerValue ); + info->date = mpw_timegm( headerValue ); mpw_free_strings( &headerName, &headerValue, NULL ); continue; @@ -580,7 +580,7 @@ static MPMarshalledUser *mpw_marshal_read_flat( return NULL; } MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; - time_t siteLastUsed = mpw_mktime( str_lastUsed ); + time_t siteLastUsed = mpw_timegm( str_lastUsed ); if (!siteLastUsed) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; return NULL; @@ -650,7 +650,7 @@ static void mpw_marshal_read_json_info( if (fileFormat < 1) return; info->redacted = mpw_get_json_boolean( json_file, "export.redacted", true ); - info->date = mpw_mktime( mpw_get_json_string( json_file, "export.date", NULL ) ); + info->date = mpw_timegm( mpw_get_json_string( json_file, "export.date", NULL ) ); // Section: "user" info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent ); @@ -707,7 +707,7 @@ static MPMarshalledUser *mpw_marshal_read_json( *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) }; return NULL; } - time_t lastUsed = mpw_mktime( str_lastUsed ); + time_t lastUsed = mpw_timegm( str_lastUsed ); if (!lastUsed) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) }; return NULL; @@ -760,7 +760,7 @@ static MPMarshalledUser *mpw_marshal_read_json( MPResultType siteLoginType = (MPResultType)mpw_get_json_int( json_site.val, "login_type", MPResultTypeTemplateName ); unsigned int siteUses = (unsigned int)mpw_get_json_int( json_site.val, "uses", 0 ); str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL ); - time_t siteLastUsed = mpw_mktime( str_lastUsed ); + time_t siteLastUsed = mpw_timegm( str_lastUsed ); if (!siteLastUsed) { *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; return NULL;