Skip over navigation

The Lost continent of

You've found a bug on my site!

Thte surest way to corrupt a youth is to instruct him to hold in higher esteem those who think alike than those who think differently

Nietzsche

Using POSIX Epoch Timestamps

At the tone, it will be 1,416,600,078 o'clock

The POSIX Epoch is a standard for measuring time using a simple count of seconds from an arbitrary point, rather than messing around with timezones, hours and minutes, months and years. POSIX Epoch timestamps are a fantastic way to store and maniuplate time values 'behind the scenes' in computer programming.

Sundial

What are they?

Briefly, they are the number of seconds elapsed since midnight Coordinated Universal Time (UTC) of January 1, 1970. Why 1970? It's completely arbitrary — it had to be some time, and that was the day chosen by the first implementors of UNIX, the first user of the POSIX Epoch. It doesn't really matter: it's just the count that counts.

Why use them?

I've been using Epoch timestamps in my programming work for most of my career. As just a single integer, it's a very natural way of dealing with time and dates using a computer. They are efficient, and make dealing with the usually very messy details of dates and timezones extremely easy. Timestamp comparisons (and therefore sorting) ary easy and fast, as is date arithmetic. Finally, converting to and from more human friendly date and time formats, in any timezone is trivial, thanks to universally available libraries.

Representation

The Epoch is traditionally stored using just a signed 32-bit integer – that's just four bytes! The clumsy looking ISO8601 format (eg. '2009-03-04T16:34:54Z') uses twenty, five times as much space! To be fair, 32-bits isn't really enough anymore. The 4 billion seconds 32-bits can represent only covers the 137 years from 1901 to 2038 (see The Y2k38 Problem). We should all be switching to 64-bit timestamps. That handles dates and times to the second from about 300 billion BC to 300 billion AD. Well before the big bang, and well past the projected heat death of the Universe.

An alternative I've been playing with is using a 64-bit float instead. It uses the same amount of space, but can store common times exactly, and with more precision. Recall that 64-bit floats can exactly represent any integer 53 bits long or less. We can therefore uniquely represent every single second between now and 285 million AD. Of course, times past or before that figure can still be manipulated, but with less than single second precision.

The floating-point representation has the advantage that we get sub-second precision 'free' for more contemporary dates – to about a millionth of a second as I write this. This can be quite handy for logfiles and the like, if your system clock supports such precision,

Conversion

The real strength of epoch timestamps are their flexibility. As useful as they are, they're not suitable for human consumption — you'd have to be a pretty hard-core UNIX nerd to actually read them directly (sadly, I cannot). Luckily the C standard library contains fantastic conversion routines, written by some extraordinarily precise people. Leap years are handled correctly, of course, as are leap seconds. As C is pretty much the lingua franca of programming languages, most languages expose this functionality — you can convert 'human' dates into timestamps easily, and then convert your timestamp into any date or time format you (or your client) can imagine!

Example Usage

The following sections demonstrates common timestamp usage scenarios from a few representational languages — the basic approach is the same. Timestamps are always in UTC, so we can use the same timestamp to print the time and date in any timezone we like, and in any format we like.

Using timestamps with PHP

Using timestamps with PHP is straight forward, centering on the two global functions time() and date(). Timezone handling works well, but requires changing a (hidden) global variable, so take care. Apparantly, a more OO orientated API is being developed for PHP6.

// Get current UTC timestamp as integer (or float)
$timestamp = time();
print $timestamp;
print microtime(true);
1236121751
1236121751.7062

// New Zealand timezone (my system's default)
// Print same timestamp, using different formats
print "New Zealand:";
print date('h:i:sa', $timestamp);
print date('l jS F Y', $timestamp);
print date(DATE_RFC822, $timestamp);
New Zealand:
12:09:11pm
Wednesday 4th March 2009
Wed, 04 Mar 09 12:09:11 +1300

// Change timezone and print a couple more
date_default_timezone_set('Europe/London');
print "London:";
print date('h:i:se', $timestamp);
print date(DATE_RFC822, $timestamp);
London:
11:09:11pm Europe/London
Tue, 03 Mar 09 23:09:11 +0000

Using timestamps with Python

Python's time.time() method returns a nice, precise floating-point value by default. Built-in time zone definitions are curiously absent, so we're using the popular pytz library here.

import datetime
import pytz
import time

# Get UTC timestamp
timestamp = time.time()
print timestamp
1236293265.13715

# Create Python UTC datetime objects that are 'timezone aware'
utc = datetime.datetime.utcfromtimestamp(timestamp);
utc = pytz.utc.localize(utc)

# Create a couple of timezone objects
auckland = pytz.timezone('Pacific/Auckland')
usa_east = pytz.timezone('US/Eastern')

# Format string
format = "%a %e %b %Y %r %Z"

# Print UTC time
print utc.strftime(format)
Thu 5 Mar 2009 10:47:45 PM UTC

# Print time in Auckland, New Zealand
tmp = utc.astimezone(auckland)
print tmp.strftime(format)
Fri 6 Mar 2009 11:47:45 AM NZDT

# Print time in Eastern USA
tmp = utc.astimezone(usa_east)
print tmp.strftime(format)
Thu 5 Mar 2009 05:47:45 PM EST


Using Timestamps in C

Like in PHP, timezone is a global setting (using the applications environment). Core functions are time() and strftime(). Disclaimer: my C is pretty rusty, all suggestions welcome.

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

#define SIZE 256

int main (void)
{
    char buffer[SIZE];
    char *tzone;
    time_t timestamp;
    char* format;

    // Get and print timestamp
    timestamp = time(NULL);
    printf("%i\n", (int) timestamp);

    // Format string
    format = "%a %e %b %Y %r %Z %n";

    // Print time in my default timezone (NZDT)
    strftime(buffer, SIZE, format, localtime(&timestamp));
    fputs(buffer, stdout);

    // Print UTC time
    tzone="TZ=UTC";
    putenv(tzone);
    strftime(buffer, SIZE, format, localtime(&timestamp));
    fputs(buffer, stdout);

    // Print time is the Eastern USA
    tzone="TZ=EST";
    putenv(tzone);
    strftime(buffer, SIZE, format, localtime(&timestamp));
    fputs(buffer, stdout);

    return 0;
}

1236296532
Fri  6 Mar 2009 12:42:12 PM NZDT
Thu  5 Mar 2009 11:42:12 PM UTC
Thu  5 Mar 2009 06:42:12 PM EST