Search code examples
macosdateposixlibcbsd

How to get range of dates that are representable/accepted by date(1)/mktime(3)?


On my modern 64bit Mac, the date 1901-12-14 is the earliest accepted by the following command:

date -ju -f "%F" "1901-12-14" "+%s"

I checked the source for the macOS date command here (Apple Open Source) and it is a failed mktime call that gives date: nonexistent time error for earlier dates.

I've looked over the source for mktime here (Apple Open Source) and I think that its a integer representation issue, but I'm not sure.

How can I find or compute the accepted range of dates of the date command (really of mktime)?

And if I wanted to get the Unix time for a date that can't be represented by mktime internals, what other libraries or functions can handle earlier dates?


Solution

  • The current macOS (OS X) implementation of mktime(3) has a minimum supported Unix time of INT32_MIN (-2147483648). This is because localtime.c:2102 assumes the result will fit in a 32-bit integer if the value is less than INT32_MAX:

    /* optimization: see if the value is 31-bit (signed) */
    t = (((time_t) 1) << (TYPE_BIT(int) - 1)) - 1;
    bits = ((*funcp)(&t, offset, &mytm) == NULL || tmcomp(&mytm, &yourtm) < 0) ? TYPE_BIT(time_t) - 1 : TYPE_BIT(int) - 1;
    

    Between 1901-12-14 and 1901-12-13 the Unix time dips below INT32_MIN, requires more than 32 bits, and is still less than INT32_MAX causing the function run out of bits.

    This is not much of an issue considering that values before the year 1900 are explicitly disallowed by localtime.c:2073:

    /* Don't go below 1900 for POLA */
    if (yourtm.tm_year < 0)
            return WRONG;
    

    (POLA: Principle Of Least Astonishment)

    Additionally, time_t is not required to be signed.