This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [PATCH] Ensure mktime sets errno on error (bug 23789)
* Zack Weinberg:
> On Thu, Oct 25, 2018 at 10:03 AM Albert ARIBAUD <albert.aribaud@3adev.fr> wrote:
>
>> Hmm... Posix defines the value returned by mktime to be "Seconds since
>> the Epoch" thus: <http://pubs.opengroup.org/onlinepubs/9699919799/>.
>> According to this definition, a year below 1970 makes the corresponding
>> seconds since the epoch value undefined, so I wonder whether the struct
>> tm above is not outside the allowed limits for mktime().
>
> That may be true per the letter of POSIX, but there are enough
> programs that expect to be able to use negative time_t values to work
> with dates prior to 1970 that glibc needs to support this usage
> anyway.
There is also this:
| 4.16 Seconds Since the Epoch
|
| A value that approximates the number of seconds that have elapsed
| since the Epoch. A Coordinated Universal Time name (specified in terms
| of seconds (tm_sec), minutes (tm_min), hours (tm_hour), days since
| January 1 of the year (tm_yday), and calendar year minus 1900
| (tm_year)) is related to a time represented as seconds since the
| Epoch, according to the expression below.
|
| If the year is <1970 or the value is negative, the relationship is
| undefined. If the year is >=1970 and the value is non-negative, the
| value is related to a Coordinated Universal Time name according to the
| C-language expression, where tm_sec, tm_min, tm_hour, tm_yday, and
| tm_year are all integer types:
|
| 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
<http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16>
I think the formula is only valid for the year 1970 and later years.
Running the test program below gives me:
error: tm_yday: expected 4, got 5
gmtime-before-1970: errors found for year 1968, exiting
So negative epoch values are clearly an extension over POSIX, but it's a
widely used one, I think.
Thanks,
Florian
#include <err.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
static void
check (const char *what, bool *good, int64_t expected, int64_t actual)
{
if (expected != actual)
{
printf ("error: %s: expected %" PRId64 ", got %" PRId64 "\n",
what, expected, actual);
*good = false;
}
}
int
main (void)
{
int64_t tm_sec = 1;
int64_t tm_min = 2;
int64_t tm_hour = 3;
int64_t tm_yday = 4;
for (int64_t tm_year = 120; tm_year > -500; --tm_year)
{
int64_t epoch = 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;
time_t time_epoch = epoch;
struct tm *tm = gmtime (&time_epoch);
if (tm == NULL)
err (1, "gmtime");
bool good = true;
check ("tm_sec", &good, tm_sec, tm->tm_sec);
check ("tm_min", &good, tm_min, tm->tm_min);
check ("tm_hour", &good, tm_hour, tm->tm_hour);
check ("tm_yday", &good, tm_yday, tm->tm_yday);
check ("tm_year", &good, tm_year, tm->tm_year);
if (!good)
errx (1, "errors found for year %" PRId64 ", exiting",
1900 + tm_year);
}
return 0;
}