Why is this an issue?

The old, much-derided Date and Calendar classes have always been confusing and error-prone, particularly in a multi-threaded context. Starting from Java SE 8, the built-in java.time (JSR-310) API provides a modern, immutable, and thread-safe framework that addresses these long-standing design flaws.

This applies not only to java.util.Date and java.util.Calendar, but also to their JDBC counterparts java.sql.Date and java.sql.Time, which inherit the same design problems and should be replaced with java.time types when interacting with modern JDBC drivers (JDBC 4.2+).

Note: While Joda-Time remains the standard for handling date and time in java versions prior to 8, users of newer versions should consider migration to the java.time API.

The java.time package offers specific classes for:

Class Use for

Instant

a timestamp

LocalDate

a date, without time of day, offset, or zone

LocalTime

the time of day, without date, offset, or zone

LocalDateTime

the date and time, without offset, or zone

OffsetTime

the time of day with an offset such as +02:00, without date, or zone

OffsetDateTime

the date and time with an offset such as +02:00, without a zone

ZonedDateTime

the date and time with a time zone and offset

Year

a year

YearMonth

a year and month

MonthDay

month and day

Month/DayOfWeek

enum classes for date fields

Period

a date-based amount of time, such as "2 months and 3 days"

Duration

a time-based amount of time, such as "34.5 seconds"

Clock

a clock providing access to the current instant, date and time

How to fix it

Use the java.time API instead of java.util.Calendar, java.util.Date, java.sql.Date, or java.sql.Time.

Exceptions

java.sql.Timestamp is excluded from this rule because it may still be required for compatibility with older JDBC drivers that do not support the java.time types introduced in JDBC 4.2.

Noncompliant code example

Use of java.util.Date, java.util.Calendar, java.sql.Date, or java.sql.Time

Date now = new Date();  // Noncompliant
DateFormat df = new SimpleDateFormat("dd.MM.yyyy");
Calendar christmas  = Calendar.getInstance();  // Noncompliant
christmas.setTime(df.parse("25.12.2020"));

java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());  // Noncompliant
java.sql.Time sqlTime = new java.sql.Time(System.currentTimeMillis());  // Noncompliant

Compliant solution

LocalDate nowUTC = LocalDate.now(ZoneOffset.UTC);  // gets current date in UTC
LocalDate christmas = LocalDate.of(2020, Month.DECEMBER, 25); // create date from year/month/day
ZonedDateTime nowParis = ZonedDateTime.now(ZoneId.of("Europe/Paris")); // get current time in Paris with time-zone information

LocalDate sqlDate = LocalDate.now();  // use LocalDate instead of java.sql.Date
LocalTime sqlTime = LocalTime.now();  // use LocalTime instead of java.sql.Time

java.sql.Timestamp timestamp = new java.sql.Timestamp(System.currentTimeMillis());  // Compliant - exception for legacy JDBC compatibility

Resources

Documentation