mktime shows inconsistent output

2019-08-08 21:44发布

问题:

While trying to write code which returns 24 hours less than a given time, mktime()shows inconsistent output. I calculate it similar to this: current_time(GMT) - 86400 which should return the right value. All we need to do is calculate based on the input time; we used mktime() to change the time and get the GMT time and then do the regular calculation. I included my code below.

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

int main()
{
    time_t currentTime, tempTime;
    struct tm *localTime;

    time(&currentTime);
    //localTime = localtime(&currentTime);
    localTime = gmtime(&currentTime); //get the time in GMT as we are in PDT

    printf("Time %2d:%02d\n", (localTime->tm_hour)%24, localTime->tm_min);
    localTime->tm_hour = 19; // Set the time to 19:00 GMT
    localTime->tm_min = 0;
    localTime->tm_sec = 0;
    tempTime = mktime(localTime);
    //tempTime = mktime(localTime) - timezone;

    printf("Current time is %ld and day before time is %ld\n", currentTime, (currentTime - 86400));
    printf("Current timezone is %ld \n", timezone);

    printf("New time is %ld and day before time is %ld\n",tempTime, (tempTime - 86400));
}

But when we check the output it is coming back incorrect after the call to call mktime(). Below is the output of above program.

$ ./a.out
Time 11:51
Current time is 1341229916 and day before time is 1341143516
New time is 1341284400 and day before time is 1341198000
$ ./print_gmt 1341229916
Mon Jul  2 11:51:56 2012
$ ./print_gmt 1341143516
Sun Jul  1 11:51:56 2012
$ ./print_gmt 1341284400
Tue Jul  3 03:00:00 2012
$ ./print_gmt 1341198000
Mon Jul  2 03:00:00 2012
$ date
Mon Jul  2 04:52:46 PDT 2012

Now if we un-comment the line which subtracts the timezone (present in time.h), then the output is as expected. Below is the value for timezone in above program

$ ./a.out
. . .
Current timezone is 28800
. . .

So why is there such inconsistent behavior for mktime() although the man pages do not mention such adjustment of the timezone. Is there something we are missing while doing such conversions?

Thanks in advance.

回答1:

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(&currentTime);

    // get the time in GMT as we are in PDT
    brokenDownTime = gmtime(&currentTime);
    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(&currentTime);
    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(&currentTime);
    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(&currentTime);
    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


回答2:

mktime(3) uses current timezone to perform the conversion. This is somewhat cryptically written in the standard:

Local timezone information shall be set as though mktime() called tzset().



回答3:

The mktime specification states that its argument represents “broken-down time, expressed as local time”. So anything you pass to mktime has to be in local time, not GMT. Your variable localTime has local in its name, but was in fact obtained from gmtime, so its result describes the curretn time in GMT. Passing that to mktime is inconsistent.



回答4:

You didn't state whether you're working on a POSIX(-like) target or some oddball system, but in the former case, you can just use the POSIX formula to convert broken down time to time_t:

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

Source: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15



回答5:

On some systems there is also a timegm function which should do the right thing.
I've just checked on Linux and it produces the correct value (e.g. timegm (gmtime (time)) == time).

Here's how to use it in Windows.
And here's a similar answer.



标签: c time mktime