Time is a difficult subject. Especially time zones and daylight saving time. Sounds easy? Well, take a look.
Adding layers in software development complicates the issue and every layer has its own view of time. Let’s start with an example: we write a simple application which stores time based data in a SQL database, e.g. Oracle. The table has a column named ‘at’. Since we don’t want to mess around with timezones, we use a column type without timezone information, in Oracle this would be ‘Date’ if we do not need milliseconds and ‘Timestamp’ if we need them. In Java with plain JDBC we can extract it with a call to ‘getTimestamp’:
Date timestamp = resultSet.getTimestamp("at");
The problem is now we have a timestamp in our local timezone. Where is it converted? Oracle itself has two timezone settings: for the database and for the session. We can query them with:
select DBTIMEZONE from dual;
and
select SESSIONTIMEZONE from dual;
First Oracle uses the time zone set in the session, then the database one. The results from those queries are interesting though: some return a named timezone like ‘Europe/Berlin’, the other return an offset ‘+01:00’. Here a first subtle detail is important: the named timezone uses the offset and the daylight saving time from the respective timezone, the offset setting only uses the offset and no daylight saving. So ‘+01:00’ would just add 1 hour to UTC regardless of the date.
In our example changing these two settings does not change our time conversion. The timezone settings are for another column type: timestamp with (local) timezone.
Going up one layer the JDBC API reveals an interesting tidbit:
Timestamp getTimestamp(int columnIndex)
throws SQLException
Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming language.
Sounds about right, but wait there’s another method:
Timestamp getTimestamp(int columnIndex,
Calendar cal)
throws SQLException
Retrieves the value of the designated column in the current row of this ResultSet object as a java.sql.Timestamp object in the Java programming language. This method uses the given calendar to construct an appropriate millisecond value for the timestamp if the underlying database does not store timezone information.
Just as in Oracle we can use a named timezone or an offset:
Date timestamp = resultSet.getTimestamp("at", Calendar.getInstance(TimeZone.getTimeZone("GMT+1:00")));
This way we have control over what and how the time is extracted from the database. The next time you work with time based information take a close look. And if you work with Java use Joda Time.
Handling time becomes much easier once you define UTC as your base unit of time. Any time is always stored and retrieved in UTC, thus moving things like Timezones/Daylight saving time into the representation layer. (Similar to saving all lenght values as meter and converting them to feet, miles or kilometers when needed).
See also: http://stackoverflow.com/a/11038176
Of course UTC is the simple solution here. The point of my post is that you should always control and not assume what time you get. And in practice you might now have control over every layer, e.g. the database.
Got a typo in the last sentence:
And in practice you might *not* have control over every layer, e.g. the database.