Why Is the Java Date API (Java.Util.Date, .Calendar) Such a Mess

Why is the Java date API (java.util.Date, .Calendar) such a mess?

Someone put it better than I could ever say it:

  • Class Date represents a specific instant in time, with millisecond
    precision. The design of this class is a very bad joke - a sobering
    example of how even good programmers screw up. Most of the methods in
    Date are now deprecated, replaced by methods in the classes below.
  • Class Calendar is an abstract class for converting between a Date
    object and a set of integer fields such as year, month, day, and hour.

  • Class GregorianCalendar is the only subclass of Calendar in the JDK.
    It does the Date-to-fields conversions for the calendar system in
    common use. Sun licensed this overengineered junk from Taligent - a
    sobering example of how average programmers screw up.

from Java Programmers FAQ, version from 07.X.1998, by Peter van der Linden - this part was removed from later versions though.

As for mutability, a lot of the early JDK classes suffer from it (Point, Rectangle, Dimension, ...). Misdirected optimizations, I've heard some say.

The idea is that you want to be able to reuse objects (o.getPosition().x += 5) rather than creating copies (o.setPosition(o.getPosition().add(5, 0))) as you have to do with immutables. This may even have been a good idea with the early VMs, while it's most likely isn't with modern VMs.

java.time vs traditional time related classes up to java 7

Oracle defined those changes and their reasons here:

Core ideas of this new API:

The new API is driven by three core ideas:

  • Immutable-value classes. One of the serious weaknesses of the existing formatters in Java is that they aren’t thread-safe. This puts the burden on developers to use them in a thread-safe manner and to think about concurrency problems in their day-to-day development of date-handling code. The new API avoids this issue by ensuring that all its core classes are immutable and represent well-defined values.

  • Domain-driven design. The new API models its domain very precisely with classes that represent different use cases for Date and Time closely. This differs from previous Java libraries that were quite poor in that regard. For example, java.util.Date represents an instant on the timeline—a wrapper around the number of milli-seconds since the UNIX epoch—but if you call toString(), the result suggests that it has a time zone, causing confusion among developers.

  • This emphasis on domain-driven design offers long-term benefits around clarity and understandability, but you might need to think through your application’s domain model of dates when porting from previous APIs to Java SE 8.

  • Separation of chronologies. The new API allows people to work with different calendaring systems in order to support the needs of users in some areas of the world, such as Japan or Thailand, that don’t necessarily follow ISO-8601. It does so without imposing additional burden on the majority of developers, who need to work only with the standard chronology.

Calender class gives different dates on different device

I've made some test in JDK 7 and the results don't seem to be affected by setFirstDayOfWeek. Actually, it seems to be related to the JVM default locale (java.util.Locale).

When the default locale is Japanese or Thai, the final result is March. Somehow, the JVM default locale affects the internals of Calendar, in some misterious ways that, I must admit, are beyond my understanding. But anyway, one way to fix it is to set in the Calendar the same locale you used in the output:

Calendar c = Calendar.getInstance(Locale.US);

This fixed the issue for me.

java.time API

This bizarre behaviour is one of the reasons to not use the old Calendar API (there are many others, btw).

In Android, it's possible to use the java.time API. In API level 26, the java.time classes are available. For lower levels, you can use this backport - and in this link there are instuctions to configure it in Android.

With this new API, it's much easier - and less error prone - to get what you want. To build a specific date/time, use a ZonedDateTime class, and a DateTimeFormatter to convert it to a String in a specific format:

// April 2nd 2018, 10 AM
ZonedDateTime dt = ZonedDateTime.of(2018, 4, 2, 10, 0, 0, 0, ZoneId.systemDefault());
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEEE MMM dd yyyy 'at' hh:mm a", Locale.US);
String formattedDate = dt.format(fmt); // Monday Apr 02 2018 at 10:00 AM

Note that the day of week is adjusted automatically, based on the day/month/year values provided.

To get the current date/time, just use ZonedDateTime.now(ZoneId.systemDefault()) (to get the current date/time in JVM's default timezone), or use ZoneId.of("zone name") to use a specific timezone - replace "zone name" with any valid IANA zone's names.

Is there official documentation by Oracle saying we should not use java.util.Date in new project

When java.time.* was developed, this phrase was what I wanted to add to java.util.Date:

"This class is now effectively deprecated by the Time Framework for Java."

It was in the source repository for a while, see here.

However, this change was rejected by Oracle, and as such there is no explicit deprecation of java.util.Date. However, all sensible developers should use Instant and java.time.* generally instead of java.util.Date.

Why does java.util.Date represent Year as year-1900?

Basically the original java.util.Date designers copied a lot from C. What you're seeing is the result of that - see the tm struct. So you should probably ask why that was designed to use the year 1900. I suspect the fundamental answer is "because we weren't very good at API design back when tm was designed." I'd contend that we're still not very good at API design when it comes to dates and times, because there are so many different use cases.

This is just the API though, not the storage format inside java.util.Date. No less annoying, mind you.



Related Topics



Leave a reply



Submit