I often see questions about why a date fetched from a database via JDBC has the ‘wrong’ timezone, or the ‘wrong’ time.

The basic thing that all Java programmers must understand when working with dates is that Java Date objects do not have a TimeZone. A Date represents its point in time as the number of milliseconds elapsed since the “Java Epoch”, January 1, 1970, 00:00:00 GMT.

So, a Java Date is essentially a Long. It embodies no concept of a TimeZone.

Every SQL database that I’m aware of does essentially the same thing. The epoch used may be different, but virtually all databases store dates in a similar timezone-free format. Your JDBC driver is responsible for doing the conversion when you insert or select a date. When you select a date from a database, you get a timezone-free representation of that date, just as if you had created that date in Java.

The part that confuses people is that they typically print the date, to the console, to a log file, or a web page, to see its value. And whether you are doing this in your sql console, or in a Java application, printing a date means formatting the date. Formatting the date necessarily introduces a TimeZone.

The really confusing part is that the most naive way of printing / formatting a date uses the ‘default’ TimeZone, and does not make this fact obvious. So the novice prints the date, sees an unexpected time zone, and assumes that the Date has the ‘wrong’ time zone. What has really happened is that the default timezone isn’t what they expected it to be.

We also see folks formatting the date, with an explicit TimeZone, and then re-parsing that string back into a Date object, thinking that they have somehow changed the Date’s TimeZone. But, as I mentioned, the Date doesn’t have a TimeZone. So then they wonder why the TimeZone hasn’t changed.

The bottom line: Remember that Dates don’t have TimeZones. Producing a String from a Date (including implicit or explicit use of toString()) without specifying a TimeZone always formats that date string using the local default TimeZone. The TimeZone does not come from the Date. It comes from the local system. What is your default TimeZone? See TimeZone.getDefault()

Here is a small example that illustrates formatting of a Date with TimeZone:

import java.util.Date;
import java.util.TimeZone;
import java.text.SimpleDateFormat;

public class DateFormatExample {
  public static void main(String[] args) {

     SimpleDateFormat formatNY = new SimpleDateFormat();
     formatNY.setTimeZone(TimeZone.getTimeZone("America/New_York"));
   
     SimpleDateFormat formatLA = new SimpleDateFormat();
     formatLA.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));

     Date now = new Date();

     System.out.println("The current date and time in NY is " + formatNY.format(now));
     System.out.println("The current date and time in LA is " + formatLA.format(now));
  }
}

For extra points, replace ‘new Date();’ with ‘new Date(0)’, and see what the moment of the Java Epoch looks like in these time zones. For even more extra points, store a date in your favorite database, select it back, and format it as above. Convince yourself that Dates are TimeZone-free.


An Improvement - the Calendar class

If you look at the JavaDocs for the Date class, you’ll see that most of its methods have been deprecated, with the notable exceptions being the constructor and the toString() method. This is because those now-deprecated methods lacked support for localization.

The java.util.Calendar class was an attempt to replace the Date class with a better implementation. The methods in the Date class that were deprecated have been re-implemented in the Calendar class.

To make up for Date’s lack of a Locale, Calender has a locale property, and setLocale and getLocal methods. This allows Calendars to localized.

Calendar also has a timeZone property and associated mutator methods setTimeZone and getTimezone. It has two constructors - one that takes a TimeZone and a Locale, and one that takes no arguments.

The no-arg constructor initializes the calendar with the default time zone and the default locale. As with the use of the Date class, Calendar’s no-arg constructor using default values is the one that is most likely to be used, and the one most likely to cause confusion.

Calendar has some other improvements over Date, including a number of additional date manipulation and comparison methods. Finally, you can set the date of a Calendar with the setTime() method, and retrieve the date of a Calendar with the getTime() method.

Calendar is now the preferred class to use, and Date should only be used when interfacing with “legacy” code, such as JDBC.


An Alternative - Joda Time

Early on, it became apparent that Java’s Date class just doesn’t have a lot of the functionality that programmers commonly need when dealing with dates. One of the most wished-for features was (and still is) a Time class that is independent from Date. One might, for instance, wish to represent “4:00 PM”, and perhaps compare it to “2:00 AM” to see which is earlier.

Joda Time appeared around 2003 to solve this and many of the other problems with java.util.Date. (Joda provides times irrespective of dates with the Instant class.) When Calendar arrived on the scene, it improved things a bit, but Joda was still needed. Joda is still actively maintained, and as of the end of 2012, is still arguably the best way to handle dates in Java. Notably, Joda even includes support for Hibernate.


Coming Improvements - JSR-310

In 2007, folks from Google, Sun, Red Hat, and others got together with Stephen Colebourne, founder and leader of the Joda project, and produced JSR-310, a Java Specification for a new Date and Time API. JSR-310 is heavily influenced by the Joda Time library, but is not exactly the same.

JSR-310 was initially slated for implementation in Java 7, and now is a candidate for inclusion in Java 8. This is sorely needed in Java - let’s hope that JSR-310 makes it in the Java 8 release!