1

I'm aware that Java 8 has a much improved date and time library based on Joda Time, but I'm very curious about the decisions made in the old libraries. I haven't found any good explanation about the java.util.Date constructor deprecation rationale (the closest question I've found is this: Difference between new Date() and Calendar date but it doesn't ask about deprecated methods/constructors and doesn't have an accepted answer).

The constructor java.util.Date(year, month, day) is considered deprecated and we should use new GregorianCalendar(year + 1900, month, date). If we call getTime() (which returns a Date...) on the Calendar instance, what have we gained, other than avoiding a deprecated constructor? Even java.sql.Date.toLocalDate uses some deprecated methods internally.

I have a codebase littered with this pattern (new GregorianCalendar followed by getTime) just to avoid the deprecated methods of java.util.Date (I need java.util.Date and java.sql.Date in JPA and JDBC), but I'm not sure what's the point or what was the point back then (*).

(*) Nowadays I can finally change them all to LocalDate because that's what I really needed anyway — saving what the user typed in without any timezone conversions.

marcus
  • 5,041
  • 3
  • 30
  • 36
  • 3
    `Date` has the nuts design of the year being the number of years after 1900, rather than the recognizable number. What have you gained? A marginally more sensible API. (The fact that month uses 0 for January in both is... depressing). – Andy Turner Oct 17 '18 at 15:35
  • Agreed. Let's see if there's something else :-) – marcus Oct 17 '18 at 15:47
  • There is, as explained in the [documentation](https://stackoverflow.com/a/52858843/5221149). – Andreas Oct 17 '18 at 15:55
  • 1
    The legacy date-time classes have a long list of problems, flaws, and awful design choices. I suggest not falling into the rabbit hole of studying them, except to learn how *not* to design your classes. Spend that time more wisely by studying *java.time*, and never look back. – Basil Bourque Oct 17 '18 at 18:45
  • Even though there are good and elaborate answers, the only one that really addresses the main point is @AndyTurner 's comment. I guess they simply thought it was too broken to be saved and didn't add more constructors and methods (getFullYear, for example). Too bad JDBC had to live with it until 4.2! – marcus Oct 17 '18 at 19:55
  • “too broken to be saved” is spot-on correct. `Calendar` was already a failed attempt to make good on `Date`. [Sun](https://en.wikipedia.org/wiki/Sun_Microsystems), [Oracle](https://en.wikipedia.org/wiki/Oracle_Corporation), and the [JCP](https://en.wikipedia.org/wiki/Java_Community_Process) community [all gave up](https://jcp.org/en/jsr/results?id=5639) on these old date-time classes with the adoption of [JSR 310](https://jcp.org/en/jsr/detail?id=310) back in 2014-02. So should you. – Basil Bourque Oct 17 '18 at 20:54
  • Yep, I'm moving to java.time bit by bit in every source file I need to change anything – marcus Oct 17 '18 at 21:00
  • Related on how to use LocalDate with JDBC: https://stackoverflow.com/questions/32548331/missed-opportunity-to-fix-jdbc-date-handling-in-java-8 — It's a mess, but a smaller mess than converting to java.sql.Date – marcus Oct 19 '18 at 14:31

2 Answers2

3

See second paragraph in the javadoc of java.util.Date:

Prior to JDK 1.1, the class Date had two additional functions. It allowed the interpretation of dates as year, month, day, hour, minute, and second values. It also allowed the formatting and parsing of date strings. Unfortunately, the API for these functions was not amenable to internationalization. As of JDK 1.1, the Calendar class should be used to convert between dates and time fields and the DateFormat class should be used to format and parse date strings. The corresponding methods in Date are deprecated.

So, to answer your question "What have we gained?", the answer is "support for internationalization":

  • Ability to specify time zone (using Calendar).

  • Ability to use non-Gregorian calendar (using Calendar).

  • Ability to use localized formatting and parsing of date strings (using DateFormat).

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Could you complement your answer regarding the pattern `new GregorianCalendar(y, m, d).getTime()`? I still need instances of Date (java.util or java.sql) to interface with databases (JPA or JDBC), so most of these advantages are thrown away in the conversion, aren't they? – marcus Oct 17 '18 at 15:59
  • 1
    @marcus Do all your work, all your business logic, all your string generation, in *java.time*. At the last moment, when you must have an object of those terrible old legacy classes, convert. Call new conversion methods `from` and `to…` added to the *old* classes. Look to `GregorianCalendar` to convert to/from `ZonedDateTime`. Look to `java.util.Date` to convert to/from `Instant`, both representing a moment in UTC. Also, as of JDBC 4.2, we can exchange *java.time* objects directly with the database, no need for the legacy classes. – Basil Bourque Oct 17 '18 at 16:13
1

The old libraries permitted the construction of java.util.Date items from entries in a Gregorian calendar, by passing in the year, month, day, etc items.

This was problematic for a number of reasons. First, the Gregorian calendar system is a hybrid calendar system that transitioned from the Julian calendar system. This transition included the need to "skip" dates to realign the Julian calendar system with the seasons. So, there are missing days. Surprisingly, the java.util.Date does a decent job of capturing this behavior, except:

  • The dates to be skipped are dependent on when the transition was adopted, which mostly maps out to be Locale dependent.
  • The strong binding to the Gregorian Calendar of the core java.util.Date object means that implementing other calendar systems is problematic, as you need to implement them on top of a Gregorian System.
  • The date being tied to Locale and TimeZone also meant that you had to adjust the platform's Locale and TimeZone to get the appropriate Date behavior you wished, often adjusting it back for out-of local date computations.

The new calendar system attempts to avoid this by:

  • Having an API that passes in a field to set with the value, preventing direct binding of the calendar fields to the API methods.
  • Having an API that permits subclassing a Calendar such that one could implement calendars with vastly different definitions of months, days, and years (think lunar calendars, Jewish calendars, Arabic Calendars, Chinese Calendars, etc).

Going forward, one should use java.util.Date only as a thin wrapper around a timestamp, and that's more to have compatibility with the older APIs. All Date manipulations should be done in the appropriate Calendar instance.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • 1
    Thanks for the insight. I disagree that you should use `Calendar` at all in 2018, but that’s a different story. That was true once. – Ole V.V. Oct 17 '18 at 18:57
  • Being a thin wrapper around a timestamp, why is "wrong" (i.e. deprecated) to ask for e.g yesterday's midnight timestamp with new Date(y, m, d) and why is it "right" (not deprecated) to ask the same thing via new GregorianCalendar + getTime? The ± 1900 thing is the only reason that comes to mind. – marcus Oct 17 '18 at 19:50
  • @Marcus A `java.util.Date` (like `java.time.Instant`) is always a moment in UTC, so it cannot directly get yesterday’s midnight except in UTC. To determine midnight, you must determine a date, and to determine a date you need a time zone, and for a time zone you must use `GregorianCalendar` (now replaced by `java.time.ZonedDateTime`). Tip: Think of “first moment of today” rather than “yesterday’s midnight”. The concept of “midnight” is ambiguous and tricky. Further complicated by the fact that in some time zones, the day can begin at a time other than 00:00:00.0 such as 01:00:00.0. – Basil Bourque Oct 17 '18 at 20:49
  • I think you got caught in a detail that's not really important (yesterday's midnight) and forgot the important part: new Date(y-1900, m, d) vs. new GregorianCalendar(y, m, d).getTime(). Neither sets an hour or timezone explicitly, both of them have similar defaults, yet only one of them is deprecated. – marcus Oct 17 '18 at 20:57
  • @Marcus Actually, my comment applies fully. See [my Answer](https://stackoverflow.com/a/52866683/642706) below. – Basil Bourque Oct 18 '18 at 03:42
  • @marcus To answer your original question of why it's wrong to get yesterday's timestamp from today's timestamp... "Well, is that a 23, 24, or 25 hour day? With a leap second in it or not?" "Is that time represented in the same locale as me?" "Is it represented in the same timezone?" Even a "one day shift" can have some pretty odd presentations if it's a one day shift observed in a country that doesn't honor daylight savings when the shift is occurring in a country that does. Simply subtracting an offset won't do, and Calendar was a step in the right direction. – Edwin Buck Oct 18 '18 at 18:50
  • 1
    @OleV.V. I agree, the new `java.time.*` classes are far better (being nearly a perfect lift from Joda time), but since the question was "Date vs Calendar" I neglected to talk about the "even better than Calendar" improvements that followed. – Edwin Buck Oct 18 '18 at 18:52
  • @marcus Just to show how odd "the day before" can be in the Gregorian Calendar system, the day before Sept 14th, 1752 is Sept 2nd, 1752 (but not if you were in Bulgaria, Greece, Japan or Turkey). Moments in time cannot be mapped by offsets to other moments in time consistently (even under the same calendar) unless you use a structure that understands all the various details, like what country you're in, whether you adopted a proposed leap second consistently with others, etc. – Edwin Buck Oct 18 '18 at 18:59