Okay, after many hours of battling with MySQL and PostgreSQL, I’ve come to the conclusion that databases support for dates and times suck. PostgreSQL appears to do a much better job than MySQL, but overall they both leave a lot to be desired. The main
The main issues with both these databases is that the lack the ability to store values as UTC without any timezone information. That’s right, I don’t want the database to know anything about timezones. See, once the database starts to store timezone information or worse, store dates and times without timezone information but not in UTC, you get into the situation where everything melts down if the timezone of the operating system or the database server changes. You also get into really nasty issues when you migrate data across locations, for example by exporting data from a server in San Francisco and migrating it to a server in London.
If you think about it, databases are primarily used for storing and retrieving data. In 99.9999% of applications, the database is NOT responsible for displaying data. For this reason, the database should not know anything about timezones because these are purely for display purposes. The correct way to store date and time data is at UTC.
So, what’s my solution given the horrible date and time support in databases? Don’t use them and use bigint instead. Store everything using the number of milliseconds since epoch in UTC. Never ever do any timezone math with your dates and times. Handle everything in UTC and only do timezone manipulations just before you display the data to a user.
I pulled this off by changing all of my datetime and timestamp columns to be bigint instead. They now look like this:
create table foo (
insert_instant bigint not null,
update_instant bigint not null
);
Additionally, in my application code when I use JDBC I pull the data out as Longs like this:
// using Joda here - if you don't use Joda you should
ResultSet rs = ...;
long insertInstant = rs.getLong(1);
DateTime dt = new DateTime(insertInstant);
Lastly, I wrote a Hibernate UserType that converts from longs to Joda DateTime instances and annotate my entities like this:
@Entity
public class Foo {
@Column(name = "insert_instant", updatable = false)
@Type(type = "com.inversoft.cleanspeak.api.domain.hibernate.DateTimeType")
public DateTime insertInstant;
}
And that’s it. It works perfectly and I never have to worry about any of my data getting screwed up if someone changes the system timezone or migrates data from one server to another. Furthermore, I finally get full support for milliseconds on MySQL and I also can add support to Clean Speak for new databases easily.