Why Avoid java.util.Date and How to Migrate to java.time APIs
The article explains the design flaws of java.util.Date, why it should be replaced, and provides a step‑by‑step guide with code examples for migrating to the modern java.time classes such as Instant, LocalDateTime, LocalDate, and ZonedDateTime in Java backend projects.
Problem with java.util.Date
Misleading name – it represents an instant, not a date.
Non‑final class encourages bad inheritance (e.g., java.sql.Date).
Mutable – leads to defensive copies.
Implicitly uses the system default time‑zone in methods like toString() .
Month index starts at 0 and year is offset from 1900, causing off‑by‑one errors.
Ambiguous method names (e.g., getDate() returns day of month, getDay() returns day of week).
Leap‑second handling is unclear.
Lenient parsing allows impossible dates.
Why change? The code‑defect scanning rule flags usage of java.util.Date and java.sql.Date as mandatory defects that block releases.
Migration strategy
1. Map database date fields to appropriate types
If the field stores date + time → use LocalDateTime .
If it stores only a date → use LocalDate .
If it stores only a time → use LocalTime .
If it stores a timestamp with zone → use Instant or ZonedDateTime .
2. Update data‑object classes
Replace Date fields with the appropriate java.time types.
3. Refactor utility methods
Replace old date creation
Date nowDate = new Date();
Date nowCalendarDate = Calendar.getInstance().getTime();with
// Instant represents a point in time, similar to Date
Instant nowInstant = Instant.now();
// For date‑time without zone
LocalDateTime nowLocalDateTime = LocalDateTime.now();
// For zone‑aware operations
ZonedDateTime nowZonedDateTime = ZonedDateTime.now();
// Convert back to java.util.Date if needed
Date nowFromDateInstant = Date.from(nowInstant);
// Convert to java.sql.Timestamp
java.sql.Timestamp nowFromInstant = java.sql.Timestamp.from(nowInstant);Rewrite common helpers
Example – date formatting:
public static String dateFormat(LocalDateTime date, String pattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return date.format(formatter);
}Example – adding time units:
public static LocalDateTime addSecond(LocalDateTime date, int second) { return date.plusSeconds(second); }
public static LocalDateTime addMinute(LocalDateTime date, int minute) { return date.plusMinutes(minute); }
public static LocalDateTime addHour(LocalDateTime date, int hour) { return date.plusHours(hour); }
public static LocalDateTime addDay(LocalDateTime date, int day) { return date.plusDays(day); }
public static LocalDateTime addMonth(LocalDateTime date, int month) { return date.plusMonths(month); }
public static LocalDateTime addYear(LocalDateTime date, int year) { return date.plusYears(year); }Example – converting a date to Chinese weekday:
public static final String[] WEEK_DAY_OF_CHINESE = new String[]{"周日","周一","周二","周三","周四","周五","周六"};
public static String dateToWeek(LocalDate date) {
DayOfWeek dayOfWeek = date.getDayOfWeek();
return WEEK_DAY_OF_CHINESE[dayOfWeek.getValue() % 7];
}Example – start/end of day:
public static LocalDateTime getStartTimeOfDay(LocalDateTime date) {
if (date == null) return null;
return date.toLocalDate().atStartOfDay();
}
public static LocalDateTime getEndTimeOfDay(LocalDateTime date) {
if (date == null) return null;
return date.toLocalDate().atTime(LocalTime.MAX);
}Example – range check:
public static Boolean betweenStartAndEnd(Instant nowTime, Instant beginTime, Instant endTime) {
return nowTime.isAfter(beginTime) && nowTime.isBefore(endTime);
}Conclusion
The migration is not difficult conceptually but requires careful, comprehensive changes across entities, converters, and DTOs; a single missed update can cause interface errors or application startup failures.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.