static char *
time_for_ttx(void)
{
- time_t now = 0;
+ struct tm *timeptr, tm;
uintmax_t epochumax;
- struct tm *timeptr;
char *epochstr, *endptr, *timestr;
/* Work out what timestamp to use. */
if ((epochstr = getenv("SOURCE_DATE_EPOCH")) != NULL) {
- /*
- * Correctly handling SOURCE_DATE_EPOCH is
- * surprisingly fiddly. To make life slightly easy,
- * we assume that we're on a POSIX system, where
- * time_t is an integer type, and that we can reject
- * negative values out of hand.
- *
- * Even given that, time_t might be any integer type.
- * It might be signed or unsigned, and it might be a
- * standard or an extended type.
- *
- * If time_t is unsigned, it's always safe to convert
- * something to it. If the value is within the range
- * of time_t it will remain unchanged, and if it
- * isn't, it'll be reduced to be within the range (C11
- * 6.3.1.3).
- *
- * While it's well-known that signed integer overflow
- * in C causes undefined behaviour, this doesn't apply
- * to conversion. According to C11 6.3.1.3, if a
- * conversion overflows "either the result is
- * implementation-defined or an implementation-defined
- * signal is raised." I can't find any clear
- * definition of what might happen when such a signal
- * is raised, but I think it must either cause the
- * process to exit with an error or do nothing (and
- * presumably leave the destination untouched).
- *
- * So having initialised the destination to 0, we can
- * assume that after the assignment it will either
- * have the correct value or have a different value
- * within the range of time_t. Then we just have to
- * check it.
- */
errno = 0;
epochumax = strtoumax(epochstr, &endptr, 10);
if (!isdigit((unsigned char)epochstr[0]) ||
endptr == epochstr || *endptr != '\0' ||
- errno == ERANGE) {
+ errno == ERANGE ||
+ /* Limit ourselves to 2000 to 10000. */
+ epochumax < 946684800 || epochumax >= 253402300800) {
fprintf(stderr, "Invalid SOURCE_DATE_EPOCH\n");
return NULL;
}
- now = epochumax;
- /*
- * Check if the value fitted into time_t. If "now" is
- * negative then it obviously didn't. If it's
- * non-negative then converting back into a uintmax_t
- * will not change the value, since all non-negative
- * numbers that can be represented in any integer type
- * can be represented in a uintmax_t.
- */
- if (now < 0 || (uintmax_t)now != epochumax) {
- fprintf(stderr, "Invalid SOURCE_DATE_EPOCH\n");
- return NULL;
+ epochumax -= 946684800; /* Rebase to 2000. */
+ tm.tm_isdst = -1;
+ tm.tm_sec = epochumax % 60;
+ tm.tm_min = epochumax / 60 % 60;
+ tm.tm_hour = epochumax / 3600 % 24;
+ long day = epochumax / 86400;
+ tm.tm_wday = (day - 1) % 7;
+ int y = 2000 + (day / 146097) * 400;
+ day = day % 146097;
+ bool ly;
+ for (;;) {
+ ly = y % 400 ? y % 100 ? y % 4 ? false:true:false:true;
+ if (day < 365 + ly) break;
+ day -= 365 + ly; y++;
}
+ tm.tm_year = y - 1900;
+ tm.tm_yday = day;
+ static int const mlc[] = {31,28,31,30,31,30,31,31,30,31,30,31};
+ static int const mll[] = {31,29,31,30,31,30,31,31,30,31,30,31};
+ tm.tm_mon = 0;
+ for (;;) {
+ int md = (ly ? mll : mlc)[tm.tm_mon];
+ if (day < md) break;
+ day -= md; tm.tm_mon++;
+ }
+ tm.tm_mday = day + 1;
+ timeptr = &tm;
} else {
- now = time(NULL);
+ time_t now = time(NULL);
if (now == (time_t)-1) {
fprintf(stderr, "Can't get current time\n");
return NULL;
}
- }
- timeptr = gmtime(&now);
- if (timeptr == NULL) {
- fprintf(stderr, "Can't convert time to UTC\n");
- return NULL;
+ timeptr = gmtime(&now);
+ if (timeptr == NULL) {
+ fprintf(stderr, "Can't convert time to UTC\n");
+ return NULL;
+ }
+ if (timeptr->tm_year >= 8100) {
+ fprintf(stderr, "Can't handle years past 9999\n");
+ return NULL;
+ }
}
timestr = asctime(timeptr);
assert(strlen(timestr) == 25);