2

I should preface this with I use Apache Spark, which uses java.sql.Date, in case anyone suggests I should use dates from java.time. The example below is in Scala.

The API that I use (which is deprecated) to get the month for a date is as follows:

val date: java.sql.Date = ???
val month = date.getMonth()

However if I look at how it appears I should do this based on the deprecation, the above code would be re-written as follows:

val date: java.sql.Date = ???
val cal = Calendar.getInstance()
cal.setTime(date)
cal.get(Calendar.MONTH)

The simplicity and readability of the code is significantly different, and the date being a side effect on the calendar is not terribly nice from a functional programming point of view. Can someone explain why they think this change was made?

Nick
  • 630
  • 7
  • 19

2 Answers2

5

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.

The JavaDoc explains. Internationalization.


"in case anyone suggests I should use dates from java.time"

There is nothing to stop you from converting to java.time classes as soon as possible, performing whatever calculations/modifications you need and, if you need to re-insert, converting back to java.sql.Date again.

Michael
  • 34,340
  • 9
  • 58
  • 100
  • Thanks for the explanation there! I often do conversions to java.time where it makes things easier, but in some instances having extra objects has negative performance implications (for reference, Apache Spark is a big data processing tool, hence the concern over having to convert objects to different types if not necessary). – Nick Jul 09 '20 at 14:03
2
    val date: java.sql.Date = ???
    val month = date.toLocalDate().getMonthValue()

You said it yourself, and I still think: You should use java.time, the modern Java date and time API. When you get an old-fashioned java.sql.Date from a legacy API not yet upgraded to java.time, convert it to a modern LocalDate and enjoy the natural code writing with java.time.

Why were getMonth() and the other getXxx methods deprecated?

While Michael has already answered the question with respect to java.util.Date, I have something to add when it comes to java.sql.Date. For this class the situation is quite a bit worse than what Michael reported.

What is left undeprecated (apprecated?) of java.util.Date after the deprecations is that a Date is a point in time. java.sql.Date on the other hand was never meant to be a point in time. One way to illustrate this fact is that its toInstant method — which should convert it to an Instant, a point in time — unconditionally throws an UnsupportedOperationException. A java.sql.Date was meant to be a calendar date to be used with an SQL database and its date datatype, which in most cases is also a date, defined by year, month and day of month. Since a Date is no longer year, month and day of month, they have virtually deprecated everything that a java.sql.Date was supposed to be. And they didn’t give us a replacement until with JDBC 4.2 we can exchange LocalDate objects with SQL databases.

The observations that lead to deprecation have got very practical consequences. Let’s try this (in Java because it is what I can write):

void foo(java.sql.Date sqlDate) {
    System.out.println(sqlDate);
    TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Pacific/Samoa")));
    System.out.println(sqlDate.getMonth());
}

In one call the method printed:

2020-11-02
9

So we had the 2nd day of the 11th month, and month prints as 9? There are two things going on:

  1. Confusingly the month number that getMonth() returns is 0-based, so 9 means October.
  2. The Date is internally represented as a count of milliseconds since the epoch to the start of the day in the default time zone of the JVM. 2020-11-02 00:00:00 in my original time zone (set to Pacific/Kiritimati for this demonstration) is the same point in time as 2020-10-31 23:00:00 in Samoa. Therefore we get October.

You don’t have to change the time zone yourself for this to happen. Situations where it can happen include:

  • The default time zone of the JVM can be changed from any part of your program and from any other program running in the same JVM.
  • The date may be serialized in a program running in one JVM and deserialized in a different JVM with a different time zone setting.

BTW the first snippet I presented at the top often won’t help against unexpected results in these situations. If things go off track before you convert from java.sql.Date to LocalDate, the conversion too will give you the wrong date. If you can make it, convert to LocalDate before anyone messes with the JVM time zone setting and be on the safe side.

Ole V.V.
  • 65,573
  • 11
  • 96
  • 117