Leap seconds and Datefudge

Time can be a pretty tricky thing for programs to manage. In fact there is a long list of time related bugs on Wikipedia. Dealing with time is tricky for a several of reasons.

Time zones

Countries use one (or more) time zones, however time zones are not always straightforward. For example did you know that Indian standard time is 5:30 ahead of UTC?

Time zones also regularly change, for example the most recent changes made to the tzdata package on CentOS are:

* Wed Nov 23 2016 Patsy Franklin <pfrankli@redhat.com> - 2016j-1
- Rebase to tzdata-2016ij
  - Saratov region of Russia is moving from +03 offset to +04 offset
    on 2016-12-04.

* Wed Nov 02 2016 Patsy Franklin <pfrankli@redhat.com> - 2016i-1
- Rebase to tzdata-2016i:
  - Cyprus is now split into two time zones as of 2016-10-30
  - Tonga will reintroduce DST on 2016-11-06

Daylight savings time

At the time of writing just over seventy countries use daylight savings time. This can result in some interesting logs similar to the following:

[1:55:47] INFO some message before the clocks went back
[1:02:22] INFO a message after the clocks went back

Leap seconds

The speed the earth rotates at isn't constant, as a result leap seconds have periodically been added to UTC, most recently at the end of 2016. To make matters worse, leap seconds are irregularly spaced due to the irregularity of the Earth's rotation.

Testing with datefudge

datefudge is a great tool for testing time based bugs. On CentOS 7 it can be installed from EPEL:

yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install -y datefudge

Once datefudge is installed, it can be used to simulate running a program at a different time. For example if you want to simulate running a program while the while the clocks change in the UK you could do the following:

$ datefudge 2016-10-30T00:59:58z bash -c 'while true; do date; sleep 1; done'
Sun 30 Oct 01:59:58 BST 2016
Sun 30 Oct 01:59:59 BST 2016
Sun 30 Oct 01:00:00 GMT 2016
Sun 30 Oct 01:00:01 GMT 2016

The --static option can also be used to freeze time for the application:

$ datefudge --static 2016-10-30T00:59:58z bash -c 'while true; do date; sleep 1; done'
Sun 30 Oct 01:59:58 BST 2016
Sun 30 Oct 01:59:58 BST 2016
Sun 30 Oct 01:59:58 BST 2016

You can also watch last year's leap second being added:

$ datefudge 2016-12-31T23:59:58z bash -c 'while true; do date; sleep 1; done'
Sat 31 Dec 23:59:58 GMT 2016
Sat 31 Dec 23:59:59 GMT 2016
Sat 31 Dec 23:59:60 GMT 2016
Sun  1 Jan 00:00:00 GMT 2017
Sun  1 Jan 00:00:01 GMT 2017

Note: the default set of CentOS time zone files do not correct for leap seconds. To replicate the behaviour above you can copy /usr/share/zoneinfo/right/Europe/London to /etc/localtime as described in Red Hat solution 2441291.