Testing time

One of the fun things about daylight savings time is the need to test your code around the times it changes.

"Time is an illusion, lunchtime doubly so"

Douglas Adams


Firstly let us be clear time does not go backwards or jump forwards in daylight savings … time itself just ticks on like it usually does … the only thing that changes is that the representation of time on the clock changes.

Unfortunately many of the libraries we use do not make it absolutely clear about how they relate to system time and representational time. After all if you live in most places in the world there are only a couple of hours that are affected…

Perhaps we need a way to test this and other changes like leap years … and if it were convenient then we might actually do this regularly

There are a couple of wrinkles that make testing time a bit tricky.

From the perspective of the viewer of representational time the following happens (remember this is an illusion time is just going forward normally):
  • One hour is magically skipped: in Victoria we go from 01:59:59am to 3am with no seconds in between
  • One hour is aliased ie appears twice: in Victoria we go from 2:59:59am back to 2:00:00am and do it all again
So we need a tool that allows us to uniquely write down the time (epoch time is a good choice - it is unique and monotonic) and then run an arbitrary program.

Enter libfaketime (
https://github.com/wolfcw/libfaketime).

This tool replaces key library calls in the program it runs so that it gets a virtual time. An example of how we can use libfaketime demonstrating a DST transition

env LD_PRELOAD=/usr/lib/i386-linux-gnu/faketime/libfaketimeMT.so.1 \
FAKETIME_FMT="%s" FAKETIME="@1648915198" TZ="Australia/Melbourne" \
python2 a.py


The output:
2022-04-03 02:59:58
2022-04-03 02:59:58.077986
2022-04-03 02:59:59.079262
2022-04-03 02:00:00.080718
2022-04-03 02:00:01.082130
2022-04-03 02:00:02.083575
2022-04-03 02:00:03.085013
2022-04-03 02:00:04.086501
2022-04-03 02:00:05.087986
^CTraceback (most recent call last):
File "a.py", line 7, in <module>
time.sleep(1)
KeyboardInterrupt


The code a.py:
import datetime
import time
print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S %z"))
while True:
print datetime.datetime.now()
time.sleep(1)


The key benefits of this approach are:
  • Use epoch time to specify the time we want to test (this means that you can actually specify a time in the aliased representation hour)
  • You can handle most programs (the caveat is programs that dynamically load code may pick up the real time function)
  • You can test multiple versions of an interpreted language
  • No need to spin up special purpose VMs and fiddle with the system clocks

Virtualising time at the process level makes testing time a lot quicker and easier … so now there is no excuse not to test