Time is hard. Any developer who tells you differently is either lying; thinks they understand it when they really don’t; or is a bona fide Time Lord. In this series we’ll explore some of the issues that time can pose, and take a look at some tools that we can use to help make time handling a little easier.
Previously we looked at how APIs which do not clearly identify the time units they used can be problematic, and some ways to avoid these issues. However, making a mistake using those will generally result in bugs that get picked up in QA because the numbers will be orders of magnitude out. For example, if you gave a duration of
1000 to an API which required values in seconds, but you mistakenly gave a value in milliseconds, the difference is going to be one thousand times what it should be. However, time can be far more subtle than that.
To understand more let’s consider some historical examples of developers have done it badly in the past. Probably the highest profile example is what known as the Millennium or Y2K Bug. The fundamental problem was that early software developers used two digits to represent the year. The net result was that the year 1900 was indistinguishable from 2000. At the time there were predictions that computing systems throughout the world would fail when the clocks ticked over from 11:59:59 on December 31st 1999 to 00:00:00 on 1st January 2000. There were predictions that aircraft would fall out of the sky, and that banking systems globally would fail. The reality was far less dramatic, although some issues certainly did occur. One of the sadder issues was that in Sheffield, United Kingdom incorrect risk assessment results for Down Syndrome was sent to 154 pregnant women due to a miscalculation of the mother’s age. I won’t detail the outcome of this (it’s in the Wikipedia page in the above link if you’re interested), but it makes for sad reading.
Another example of a similar issue is the Unix time (also known as epoch time or Posix time) which use a long to represent an offset from 1st January 1970 (the Epoch) in seconds. The issue here is that at 19 January, 2038 03:14:08 GMT a signed 32-bit integer used to hold this value would overrun. This is commonly known as the 2038 problem and is potentially every bit as problematic as the Millennium Bug.
It’s not just about how we represent time that can be problematic. Performing time arithmetic can be tricky. A lesser-know issue which occurred in 2000 was problems arising because some system incorrectly calculated leap years. Most people are aware that every fourth year in the Gregorian calendar has an additional day in February to cope with the fact that a solar year is not an exact number of days – it is actually 365.24219 days. Having an extra day every four years gives a close approximation, but actually there is a further rule of which people are less aware: There is no extra day in years which are divisible by 100 (i.e.
year % 100 == 0) unless the year is also divisible by 400 (i.e.
year % 400 == 0). So 1900 is not a leap year (it is divisible by 4 and 100, but not 400) whereas 2000 is a leap year (it is divisible by 4, 100, and 400). Errors in the implementation of this logic resulted in some systems incorrectly calculating 2000 to not be a leap year when in fact it was – and there were a number of failures on February 29th 2000 which some computer systems calculated to be March 1st 2000.
These kinds of problem can get further compounded when we occasionally have leap seconds to make even more minor adjustments to keep calendar years in sync with solar years.
There are also many more examples of time calculations gone awry. On a particular project I worked on, there was an issue when a date range straddled a year boundary. The problem was because the year was being ignored, and so the duration of the time span calculation resulted in a negative number. This caused crashes for a few days until the date range cleared the year transition.
So how can we try to avoid these issues? Unfortunately there is not simple answer, but there is one pretty fundamental rule: Do not try to roll your own date / time handling routines – you are highly likely to get bitten, and bitten very badly if you do. Instead go for mature, well tested, and well used libraries which will handle all of this subtle complexity for you.
Traditionally on Android we have had
java.util.Calendar to do this for us, but these can be problematic. They are not thread-safe, and they also have some confusing APIs. A newer addition is Joda-Time which adds thread safety, and some much cleaner APIs. However this has now been superseded by the new Date APIs in Java 8 which are an implementation of JSR 310, which are available in Android O onwards.
For those of us not in a position to specify
minSdkVersion 26 in our projects there is a backport of JSR 310 which has been optimised for Android by Jake Wharton (who else!). There is also a Android-optimised port of Joda-Time by Dan Lew.
If starting a project from scratch, going with JSR 310 is by far the better option because Joda-Time is no longer under active development – there will be no more major releases since JSR 310 now fills the gap that it once served. However, if you are already using Joda-Time in a project there would be no great problem with sticking with it.
The JSR 310 APIs are actually quite similar to the Joda-Time APIs and I’ll focus on the JSR 310 ones when we start diving in to the APIs in the next article.
There have been no code changes in this article, but the source for the previous part is available here.
© 2017, Mark Allison. All rights reserved.
Time for non-Time Lords – Part 2 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.