mktime Only Handling Leap Years on Clang?

2019-08-12 02:41发布

问题:

In this answer I proposed that marihikari use the standard functionality of mktime rather than trying to implement his own Gregorian calendar system.

I wrote this function to demonstrate how mktime could be used to accomplish this:

bool leap_year(int year) {
    tm bar = { 0, 0, 0, 29, 1, year - 1900 };

    mktime(&bar);

    return bar.tm_mday == 29 && bar.tm_mon == 1 && bar.tm_year == year - 1900;
}

Testing this with:

cout << "2000: " << leap_year(2000) << "\n2001: " << leap_year(2001) << "\n2004: " << leap_year(2004) << "\n1900: " << leap_year(1900) << "\n2100: " << leap_year(2100) << endl;

Yielded the correct result in Clang 3.7.0:

2000: 1
2001: 0
2004: 1
1900: 0
2100: 0

But an incorrect result in gcc 5.1.0:

2000: 1
2001: 0
2004: 1
1900: 1
2100: 1

And an incorrect result in Visual Studio 2015:

2000: 1
2001: 0
2004: 1
1900: 1
2100: 0

I assume this is a bug in gcc 5.1.0 and Visual Studio 2015?

回答1:

mktime:

Converts local calendar time to a time since epoch as a time_t object. time->tm_wday and time->tm_yday are ignored. The values in time are permitted to be outside their normal ranges.
...
If the conversion is successful, the time object is modified. All fields of time are updated to fit their proper ranges.

mktime will return:

Time since epoch as a time_t object on success or -1 if time cannot be represented as a time_t object.

It is not specified however what efforts the implementation must make to convert the tm. So as long as the time is converted or static_cast<time_t>(-1) is returned the requirements for mktime have been filled.

Meaning that the following function will work on all platforms that correctly support mktime:

bool leap_year(int year) {
    tm bar = { 0, 0, 0, 29, 1, year - 1900 };

    return static_cast<time_t>(-1) != mktime(&bar) && bar.tm_mday == 29 && bar.tm_mon == 1 && bar.tm_year == year - 1900;
}