Every significant java system I have worked on has had at least one problem with dates. Often it is to do with the problems discussed in DateAndTime
, but it's also to do with the assumption that because java.util.Date along with java.text.FormatDate?
and java.util.Calendar does everything you require, you should use them raw.
While it is true that they do everything required, the problems come from the fact that they do a whole lot more as well. They do a whole load of stuff with timezones, daylight saving and locales, often using the local machine's defaults. These defaults will vary from machine to machine. Even if a batch of machines were the same when they were bought, the users will tinker with the timezone just to see what it does.
Conversions happen as a date is transferred from one machine to another, or from one format to another. Rarely is all the information to successfully do the conversion carried with the date. Neither do we agree a common set of information before we start. So as a date is transferred between a client and a server (possibly as XML), or a server and the database, a period equivalent to one or more daylight saving or timezone offsets is added or subtracted. Sometimes this only suddenly happens after the system has been in service for some time, or when a new machine is added to the network, or there has been a software upgrade.
For some reason, we just never seem to think these things through before we decide to use java.util.Date uncut.
java.util.Date actually holds a date and a time. This is where many problems begin. We assume we can store a date in it. java.sql.Date extends java.util.Date in an attempt to reduce the functionality by removing the time portion. It doesn't succeed. You can still add a day to a date and get a point in time at 23:00 on the same day. We can argue the toss about terminology and about how it being used wrong, but the fact remains when there is so much room for confusion, there will be confusion.
What should we do instead? We shouldn't use java.util.Date, or at least not directly.
When we design systems, we need to decide what it is (out of all the many different things we could do with dates) that we actually want to do in each instance. Then we should encapsulate just that functionality in our own classes, delegating where appropriate to java.util.Date, java.util.Calendar and java.text.DataFormat?
. Importantly, we must decide how we compare, add and subtract intervals, and how we will display each type of date.
Here are a few of the different type use of dates I have seen used:
1. The recorded time something happened. This might be the time an order was received. It makes sense to display this sort of date and time to the user in their own locale/timezone/daylight saving. It also makes sense to compare these against each other to the second or millisecond. I think this what java.util.Date was really designed for. Sometimes this is called a timestamp - but even this term has different means to different people.
2. An appointment date and time. If the appointment is for a roller coaster ride at Disneyland Paris, you would expect to view the date and time with the timezone and daylight saving that will be in place when you get to Paris. To make it clear, however, the timezone should also be displayed along side it, so the date class for this must hold this timezone information.
3. Date of birth. This is a form of legal date. One's date of birth is not associated with any particular time, or even a timezone, and is always the same no matter where in the world it is viewed from. Sometimes legal information must be reproduced exactly as entered. So if the user enters DD/MM/YYYY, that is how it must be reproduced and not in MM/DD/YY no matter who is viewing it.
This problem occurs in most languages and environments. For the timezone and DST issues, the usual remedy is more rigorous definitions for storage and transfer data formats. If the database is defined as containing a date/time, there is room for interpretation. If the database is more rigorously defined as containing a UTC date/time, the problem generally goes away.
In my experience, dates should never be stored or transferred in a non-UTC representation. They should optionally be translated to and from local time for display and data entry, but always stored in UTC.
This issue isn't specific to dates and times. It's really a matter of normalizing data and canonical forms. Instead of dates and times, think about length. The user may enter them in inches or feet or centimeters or whatever else we understand. When we go to store those lengths in a database, it makes sense that we would store them all in the same units.
Yes - bitter experience tells me that it should always be the UTC value that is stored on the database when dealing with the date and time of an event. Without using UTC, comparisons using SQL become meaningless. I have recently seen an attempt to avoid daylight saving issues by storing localized times as if they were UTC (say 13:00 BST stored in a timestamp as 13:00 UTC). However, everything went wrong when calculating lead times by comparing with Oracle CURRENT_TIMESTAMP.
And yes - for date/times it should always be the UTC that is transported around a system, and only converted to and from a localized time during user interaction. However, even when we realize this, we often find that what we thought was a UTC value has actually been converted into something else.
Things are a bit scarier when dealing with just dates without times. If the user inputs preferred delivery date of 1/July/06, what do we store? Midnight in UTC, UTC for what would be midnight in local time with daylight saving, or a just a number of days since 1 Jan 1AD? What happens when we want to compare the date/time of an available delivery slot with this date? Obviously, with a little thought these are all problems that can be solved. All too often, we just don't seem to.
Usually, the solutions involve having a bit more information that just the UTC time, and storing this on the database with it. Hence my belief that a class that encapsulates the information we need in each situation is required, instead of just using java.util.Date on its own.
I believe you're now talking about JavaSqlDate?
, but no matter. Oracle has a problem in that it does not store the timezone of a date field. (Oracle date fields correspond to SqlStandard?
timestamp fields.) When you access a timezone field in an Oracle database via JDBC, it assumes that the stored time is in the timezone that is the default for the java app making the access. In other words, if you put in a date and time from a computer using one timezone, and retrieve it with a computer using another timezone, the timestamp will be shifted in absolute time by the difference between the two timezones.
I have frequently resorted to storing dates, times and timestamps as strings in the database and enforcing conversion rules using D
for an alternative.
CategoryJava CategoryTime CategoryStory