All about java.util.Date

This post is an attempt to reduce the number of times I need to explain things in Stack Overflow comments. You may well be reading it via a link from Stack Overflow – I intend to refer to this post frequently in comments. Note that this post is mostly not about text handling – see my post on common mistakes in date/time formatting and parsing for more details on that.

There are few classes which cause so many similar questions on Stack Overflow as java.util.Date. There are four causes for this:

  • Date and time work is fundamentally quite complicated and full of corner cases. It’s manageable, but you do need to put some time into understanding it.
  • The java.util.Date class is awful in many ways (details given below).
  • It’s poorly understood by developers in general.
  • It’s been badly abused by library authors, adding further to the confusion.

TL;DR: java.util.Date in a nutshell

The most important things to know about java.util.Date are:

  • You should avoid it if you possibly can. Use java.time.* if possible, or the ThreeTen-Backport (java.time for older versions, basically) or Joda Time if you’re not on Java 8 yet.
    • If you’re forced to use it, avoid the deprecated members. Most of them have been deprecated for nearly 20 years, and for good reason.
    • If you really, really feel you have to use the deprecated members, make sure you really understand them.
  • A Date instance represents an instant in time, not a date. Importantly, that means:
    • It doesn’t have a time zone.
    • It doesn’t have a format.
    • It doesn’t have a calendar system.

Now, onto the details…

What’s wrong with java.util.Date?

java.util.Date (just Date from now on) is a terrible type, which explains why so much of it was deprecated in Java 1.1 (but is still being used, unfortunately).

Design flaws include:

  • Its name is misleading: it doesn’t represent a Date, it represents an instant in time. So it should be called Instant – as its java.time equivalent is.
  • It’s non-final: that encourages poor uses of inheritance such as java.sql.Date (which is meant to represent a date, and is also confusing due to having the same short-name)
  • It’s mutable: date/time types are natural values which are usefully modeled by immutable types. The fact that Date is mutable (e.g. via the setTime method) means diligent developers end up creating defensive copies all over the place.
  • It implicitly uses the system-local time zone in many places – including toString() – which confuses many developers. More on this in the “What’s an instant” section
  • Its month numbering is 0-based, copied from C. This has led to many, many off-by-one errors.
  • Its year numbering is 1900-based, also copied from C. Surely by the time Java came out we had an idea that this was bad for readability?
  • Its methods are unclearly named: getDate() returns the day-of-month, and getDay() returns the day-of-week. How hard would it have been to give those more descriptive names?
  • It’s ambiguous about whether or not it supports leap seconds: “A second is represented by an integer from 0 to 61; the values 60 and 61 occur only for leap seconds and even then only in Java implementations that actually track leap seconds correctly.” I strongly suspect that most developers (including myself) have made plenty of assumptions that the range for getSeconds() is actually in the range 0-59 inclusive.
  • It’s lenient for no obvious reason: “In all cases, arguments given to methods for these purposes need not fall within the indicated ranges; for example, a date may be specified as January 32 and is interpreted as meaning February 1.” How often is that useful?

I could find more problems, but they would be getting pickier. That’s a plentiful list to be going on with. On the plus side:

  • It unambiguously represents a single value: an instant in time, with no associated calendar system, time zone or text format, to a precision of milliseconds.

Unfortunately even this one “good aspect” is poorly understood by developers. Let’s unpack it…

What’s an “instant in time”?

Note: I’m ignoring relativity and leap seconds for the whole of the rest of this post. They’re very important to some people, but for most readers they would just introduce more confusion.

When I talk about an “instant” I’m talking about the sort of concept that could be used to identify when something happened. (It could be in the future, but it’s easiest to think about in terms of a past occurrence.) It’s independent of time zone and calendar system, so multiple people using their “local” time representations could talk about it in different ways.

Let’s use a very concrete example of something that happened somewhere that doesn’t use any time zones we’re familiar with: Neil Armstrong walking on the moon. The moon walk started at a particular instant in time – if multiple people from around the world were watching at the same time, they’d all (pretty much) say “I can see it happening now” simultaneously.

If you were watching from mission control in Houston, you might have thought of that instant as “July 20th 1969, 9:56:20 pm CDT”. If you were watching from London, you might have thought of that instant as “July 21st 1969, 3:26:20 am BST”. If you were watching from Riyadh, you might have thought of that instant as “Jumādá 7th 1389, 5:56:20 am (+03)” (using the Umm al-Qura calendar). Even though different observers would see different times on their clocks – and even different years – they would still be considering the same instant. They’d just be applying different time zones and calendar systems to convert from the instant into a more human-centric concept.

So how do computers represent instants? They typically store an amount of time before or after a particular instant which is effectively an origin. Many systems use the Unix epoch, which is the instant represented in the Gregorian calendar in UTC as midnight at the start of January 1st 1970. That doesn’t mean the epoch is inherently “in” UTC – the Unix epoch could equally well be defined as “the instant at which it was 7pm on December 31st 1969 in New York”.

The Date class uses “milliseconds since the Unix epoch” – that’s the value returned by getTime(), and set by either the Date(long) constructor or the setTime() method. As the moon walk occurred before the Unix epoch, the value is negative: it’s actually -14159020000.

To demonstrate how Date interacts with the system time zone, let’s show the three time zones mentioned before – Houston (America/Chicago), London (Europe/London) and Riyadh (Asia/Riyadh). It doesn’t matter what the system time zone is when we construct the date from its epoch-millis value – that doesn’t depend on the local time zone at all. But if we use Date.toString(), that converts to the current default time zone to display the result. Changing the default time zone does not change the Date value at all. The internal state of the object is exactly the same. It still represents the same instant, but methods like toString(), getMonth() and getDate() will be affected. Here’s sample code to show that:

import java.util.Date;
import java.util.TimeZone;

public class Test {

    public static void main(String[] args) {
        // The default time zone makes no difference when constructing
        // a Date from a milliseconds-since-Unix-epoch value
        Date date = new Date(-14159020000L);

        // Display the instant in three different time zones
        TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
        System.out.println(date);

        TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
        System.out.println(date);

        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Riyadh"));
        System.out.println(date);

        // Prove that the instant hasn't changed...
        System.out.println(date.getTime());
    }
}

The output is as follows:

Sun Jul 20 21:56:20 CDT 1969
Mon Jul 21 03:56:20 GMT 1969
Mon Jul 21 05:56:20 AST 1969
-14159020000

The “GMT” and “AST” abbreviations in the output here are highly unfortunate – java.util.TimeZone doesn’t have the right names for pre-1970 values in all cases. The times are right though.

Common questions

How do I convert a Date to a different time zone?

You don’t – because a Date doesn’t have a time zone. It’s an instant in time. Don’t be fooled by the output of toString(). That’s showing you the instant in the default time zone. It’s not part of the value.

If your code takes a Date as an input, any conversion from a “local time and time zone” to an instant has already occurred. (Hopefully it was done correctly…)

If you start writing a method with a signature like this, you’re not helping yourself:

// A method like this is always wrong
Date convertTimeZone(Date input, TimeZone fromZone, TimeZone toZone)

How do I convert a Date to a different format?

You don’t – because a Date doesn’t have a format. Don’t be fooled by the output of toString(). That always uses the same format, as described by the documentation.

To format a Date in a particular way, use a suitable DateFormat (potentially a SimpleDateFormat) – remembering to set the time zone to the appropriate zone for your use.

40 thoughts on “All about java.util.Date”

  1. I may have drank too much POSIX kool-aid, but I find it very useful when datetime/calendar utils automatically wrap around to correct values (aka normalization? e.g. Jan 32nd->Feb 1st).

    That said I agree with everything else, and have never used anything but JodaTime and java.time. And can’t count the number of times java.sql.Date vs. java.util.Date has caused me problems…

    Like

    1. How is it useful to allow users to provide completely invalid input? Would you want array indexes to wrap too, so that if you create an array of size 5 and ask for element 7 it gives you something from the middle? The only cases where I could see it being useful is where you’re taking a valid date and saying “I just want to add 5 days”, and doing so by adding it to the day part before constructing the date-based object… and that’s better handled by using an API that will allow you to write date = new DateFoo(year, month, day).AddDays(5);. Can you give other examples of where it’s useful, considering the downside of having to do manual validation in the more common case where you really only want to accept valid dates?

      Like

  2. There is also another pitfall of DateFormat classes like SimpleDateFormat that it you can’t use them for parsing in a multi-threaded environment. The only safe way to do it is to construct a new one every time. The new API in Java 8 has thread-safe formatters and parsers.

    Like

  3. Date has caused me problems… The only cases where I could see it being useful is where you’re taking a valid date and saying “I just want to add 5 days”, and doing so by adding it to the day part before constructing the date-based object… and that’s better handled by using an API that will allow you to write date = new DateFoo(year, month, day).

    Like

  4. There is likewise another entanglement of DateFormat classes like SimpleDateFormat that it you can’t utilize them for parsing in a multi-strung condition. The main safe approach to do it is to develop another one without fail. The new API in Java 8 has string safe formatters and parsers.

    Like

    1. Not quite sure what you mean by “multi-strung condition” or “string safe” – did you mean “multi-threaded environment” and “thread-safe”? If so, I agree completely.

      Like

  5. Seems that the library maintainers are doing a good job of keeping in some of the “idiosyncrasies” described here, even in the newer classes like “Instant” (@since 1.8). From the JavaDoc, notice the 2nd line in particular. “This method allows an arbitrary number of nanoseconds to be passed in. The factory will alter the values of the second and nanosecond in order to ensure that the stored nanosecond is in the range 0 to 999,999,999.” Well, at least they are consistent :)

    Like

  6. Great post.

    However, for me personally I understood very early on in my Java career that Date is merely a wrapper around a long (ms since epoch) and you should think of it as such. I have used it in the places where I needed an Object rather than a primitive, however often forced to by some library.

    I must say I have in fact never met any Java developer who didn’t get this, unless they were only 1-2 months into learning Java or into learning programming a such. Those are the people who ask such questions on SO, in my experience. What I’m saying is that I don’t think their questions represent a greater problem with Date.

    Yes, it is misnamed (but then again many things are and the Javadoc opens by saying clearly what Date is), it is mutable (but sometimes that is handy if you really need an object and you believe the tales that there is still (to this day) a cost involved with object creation – albeit negligible – except if you are into low latency, no jitter type of stuff), it can return 61 from the getSeconds() method (but that method has been in-your-face deprecated since the yearly days of Java, never seen the method used) and the toString() method does some weird woodo (but I never seen anyone use it, except for debug output) .. and all these things may of course add up on you.

    However, the truth of the matter is that while Date may be a stumbling block for newbies, if you understand what it is, then I don’t see any reason to exchange java.util.Date for java.time.Instant in existing code. In fact, unless you need the extra precision that Instant will give you Instant will take up considerable more memory. The real choice in the past has been if you should use a long (primitive), a Long or a Date in your code. Depending on the use-case each had its purpose and benefits.

    To be honest, the problem has never been really been Date, but the lack of all the true date and time classes that java.time now luckily offers. Those are great. Calendar has been a greater screw-up than Date in my book. Also SimpleDateFormat has thrown not just newbies but seasoned Java developers off, so that one was (is!) a real mess.

    There is indeed tons of code out there where what the code wants to represent is indeed an instant in time (often named something like ‘timestamp’ or the like) and for those cases Date has always been and still is okay’ish. I’ve seen tons of code that look like this:

    Date recordCreatedAt;

    That one doesn’t hurt my eye. The name of the identifier tells me that the developer intended it as an instant in time.

    So now we have java.time.Instant. But in fact – to some degree – I think Date has bigger competitors in long or Long than it does in java.time.Instant.

    All this being said it may be I have just forgotten the early days of learning and blissfully trying to forget all the misunderstanding I did at the time. :-)

    Like

  7. Good article. The concrete example of the moon walk being an instant in time helps a lot. One suggestion would be to extend that with a more relative example, for example, dealing with a Date that was constructed from System.currentTimeMillis(). I think the point to get across would be that the time zone is irrelevant here as well, as long as the underlying system clock that the JVM uses is accurate; and by accurate, I mean the correct time/date for whatever time zone the system thinks it’s in (and maybe internally, it is always UTC). I’d like to hear a bit more about that aspect. For example, when I boot my laptop into Linux, and then back into Windows, the time is messed up for a bit, until it resyncs with the time service.

    Like

Leave a comment