When does the 64-bit Unix time_t really end?

Inspired by Numberphile‘s YouTube video on the end of (Unix) time, I began crunching some numbers, pondering the end of 64-bit time_t. Numberphile arrived at December 4th, in the year 292,277,026,596, and I wanted to know how they got that result.

Here’s my train of thought.

A 64-bit signed integer using two’s complement representation has the maximum value of 264 – 1 – 1 = 9,223,372,036,854,775,807. Thus, the 64-bit time_t counter will at some point reach this value. That value represents the number of seconds since midnight to January 1st, 1970 UTC.

A gregorian year, on average, contains 365.2425 days. Thus, expressed as seconds per average year, we get 365.2425 × 24 × 60 × 60 = 31,556,952 seconds/year.

Divide 9,223,372,036,854,775,807 seconds by 31,556,952 seconds/year, and you get 292,277,024,626.927,714,913,658… years.

Remember, the point zero on this scale represents the gregorian year 1970.0 AD. To convert the above value to a gregorian year we must add 1,970.0.

Thus, we get 1,970.0 + 292,277,024,626.927,714,913,658… = 292,277,026,596.927,714,913,658….

As such, I arrived at the year 292,277,026,596 AD, same as Numberphile.

If we discard the integer part of the year 292,277,026,596.927,714,913,658…, we’re left with a fraction of a year, 0.927,714,913,658…. Multiply this value by 365.2425 days/year, and we get 338.840,914,352 days. This represents 337.840,914,352 days past January 1st, 292,271,025,015. That should make it December 4th, 292,277,026,596.

If we discard the integer part of 338.840,914,352, we’re left with a fraction of a day, 0.840,914,352. Multiply this value by 24.0 hours/day, and we get 20.181,944,448 hours. Discard the integer part, leaving us with 0.181,944,448 hours, multiply by 60.0 minutes/hour, and we get 10.916,666,688 minutes. Again, discard the integer part, leaving us with 0.916,666,688 minutes, multiply by 60.0 seconds/minute, and we get 55.000,012,8 seconds, which we’ll happily round off to 55 seconds.

When we gather all the bits and pieces, the end date might be December 4th, 292,277,026,596 AD, at 20:10:55 UTC. Or maybe it’s December the 3rd, or the 5th.

One thing I haven’t taken into account, are the leap seconds. Surely there will be a few more, and maybe the whole idea of introducing new leap seconds will cease at some point.

I decided to put Python 3.4.3 to the test, and here’s what I got:

Python 3.4.3 (default, Mar 10 2015, 09:04:01)
[GCC 4.2.1 Compatible FreeBSD Clang 3.4.1 (tags/RELEASE_34/dot1-final 208032)] on freebsd10
Type "help", "copyright", "credits" or "license" for more information.
>>> import time
>>> time.ctime(9223372036854775807)
'Sun Dec  4 16:30:07 219250468'
>>>

Python 3.4.3, or the underlying time library, be it C or Python, claims the year will be 219,250,468. That can’t be right. At least the day and month agrees with the calculations above, but that’s about it.

A small C 2011 programme, inspired by the IDRBT Working Paper No. 9 Y2K38, with a couple of bugs removed, didn’t live up to its promise on FreeBSD/amd64 stable/10.

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

int main(void)
{
  int i;
  struct tm tm;
  time_t t = 9223372036854775800LL; // Only 8 seconds away from doom!

  memset(&tm, 0, sizeof(tm));

  for (i = 0; i < 10; i++) {
    gmtime_r(&t, &tm);

    printf("Date: %04d-%02d-%02d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
    printf("Time: %02d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);

    t++;
  }

  return 0;
}

Here’s a sample run:

Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00
Date: 1900-01-00 Time: 00:00:00

All I do know, is that I won’t be around to witness this event when it happens, nor will this blog be around.

Disclaimer: My beloved and trusted HP 48GX was not harmed in any way during this session.