Std::Mktime and Timezone Info

std::mktime and timezone info

mktime assumes that the date value is in the local time zone. Thus you can change the timezone environment variable beforehand (setenv) and get the UTC timezone.

Windows tzset

Can also try looking at various home-made utc-mktimes, mktime-utcs, etc.

how to use mktime to respect timezone

You problem is almost certainly that the tm_isdst flag of your tm struct defaulted to 0 which caused it to assume no DST even in summer (which your date is). Then when you converted back to localtime it DID add the DST offset, causing the difference you note.

The simplest and often correct solution is to set the tm_isdst member to -1 to let mktime figure out if the date in question should have a DST offset applied.

Note that whether DST is in effect is orthogonal to which timezone you're using. Both need to be set in the correct way for the results to come out correctly.

Also do consider using localtime_r instead of localtime if there's any chance of your application being threaded.

Easy way to convert a struct tm (expressed in UTC) to time_t type

Use timegm() instead of mktime()

Why does mktime() need to know about daylight saving time?

Whether DST is in effect changes what the epoch time will be because mktime uses the current time zone to determine the time.

For example, if I populate tm with 1/1/70 00:00:00 as follows:

tm.tm_sec = 0;
tm.tm_min = 0;
tm.tm_hour = 0;
tm.tm_mday = 1;
tm.tm_mon = 0;
tm.tm_year = 70;
tm.tm_isdst = 0;

I get a value of 18000 for tv because my timezone is GMT-5 (18000 = 3600 * 5). If I change the value of tm_isdst to 1, then tv will be set to 14400 (3600 * 4).

Setting tm_isdst to -1 will look at the local timezone database to see if DST is in effect for the given date/time.

mktime and tm_isdst

I believe the original reason for that is some timezones do not have daylight savings time. Since mktime is not async-safe nor is it re-entrant allows the implementation to store the current value of daylight savings in the POSIX extern char tzname[2], indexed by daylight [0 or 1]. This means tzname[0]="[std TZ name]" and tzname="[daylight TZ name, e.g. EDT]"

See your tzset() man page for more information on this. Standards conforming mktime() is required to behave as though it called tzset() anyway. This kind of obviates the use of tm_isdst, IMO.

Bottom line: your particular implementation and timezone(s) would dictate whether you would use -1, 0, or 1 for tm_isdst. There is no one default correct way for all implementations.

Incorrect time calculation with mktime to get UTC+8

You could use localtime after changing the TZ environment variable to your desired timezone:

#include <iostream>
#include <stdlib.h>
#include <time.h>

int main(){

_putenv_s( "TZ", "GMT-08:00" );

time_t mytime = time( NULL );
struct tm* mytm = localtime( &mytime );

std::cout << "Current local time and date: " << asctime(mytm);
return 0;
}

The object mytime will receive as a result of the function time() the amount of seconds since 00:00 hours, Jan 1, 1970 UTC, which is the current Unix timestamp. localtime() will use the value pointed by mytime to fill a tm structure with the values that represent the corresponding time, expressed for the local timezone.

By default, the timezone used by localtime() is generally the one used in your computer. However, you can change it with the function _putenv_s(), in which I manipulated the TZ variable and added a new definition to it GMT-08:00 which is the timezone for Hong Kong.

In POSIX systems, a user can specify the time zone by means of the TZ
environment variable.

Note that however a more standard way of manipulating TZ variable is by using the function int setenv (const char *name, const char *value, int replace) but it wasn't defined in this sample, so I used an alternative.

You can read more about TZ environment variable here

mktime shows inconsistent output

I think the problem is here:

The mktime() function shall convert the broken-down time, expressed as local time, in the structure pointed to by timeptr, into a time since the Epoch value...

Note the words local time. The C standard has them too in the description of mktime():

The mktime function converts the broken-down time, expressed as local time, in the
structure pointed to by timeptr into a calendar time value with the same encoding as
that of the values returned by the time function.

gmtime(), on the other hand, produces time in GMT/UTC and not in your time zone:

The gmtime() function shall convert the time in seconds since the Epoch pointed to by timer into a broken-down time, expressed as Coordinated Universal Time (UTC).

EDIT: If you just want 19:00 of the previous GMT/UTC day, you can do this:

#include <stdio.h>
#include <time.h>

int main(void)
{
time_t currentTime;
struct tm *brokenDownTime;

time(¤tTime);

// get the time in GMT as we are in PDT
brokenDownTime = gmtime(¤tTime);
printf("Current Time (GMT): %2d:%02d\n"
" seconds since Epoch: %ld\n",
brokenDownTime->tm_hour,
brokenDownTime->tm_min,
(long)currentTime);

// "Unwind" time to 0:00:00 (assuming time_t is an integer):
currentTime /= 24 * (time_t)3600;
currentTime *= 24 * (time_t)3600;

brokenDownTime = gmtime(¤tTime);
printf("Time at the beginning of the current GMT day: %2d:%02d\n"
" seconds since Epoch: %ld\n",
brokenDownTime->tm_hour,
brokenDownTime->tm_min,
(long)currentTime);

// Add 19 hours:
currentTime += 19 * (time_t)3600;

brokenDownTime = gmtime(¤tTime);
printf("Time at 19:00:00 of the current GMT day: %2d:%02d\n"
" seconds since Epoch: %ld\n",
brokenDownTime->tm_hour,
brokenDownTime->tm_min,
(long)currentTime);

// Subtract 1 day:
currentTime -= 24 * (time_t)3600;

brokenDownTime = gmtime(¤tTime);
printf("Time at 19:00:00 of the previous GMT day: %2d:%02d\n"
" seconds since Epoch: %ld\n",
brokenDownTime->tm_hour,
brokenDownTime->tm_min,
(long)currentTime);

return 0;
}

Output:

Current Time (GMT): 13:23
seconds since Epoch: 1341235429
Time at the beginning of the current GMT day: 0:00
seconds since Epoch: 1341187200
Time at 19:00:00 of the current GMT day: 19:00
seconds since Epoch: 1341255600
Time at 19:00:00 of the previous GMT day: 19:00
seconds since Epoch: 1341169200


Related Topics



Leave a reply



Submit