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:
- Confusingly the month number that
getMonth()
returns is 0-based, so 9 means October.
- 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.